mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics
@ 2026-11-06 11:24 Dong Yibo
  2026-11-06 11:25 ` [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support Dong Yibo
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:24 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

This patch series adds basic management and statistics support to the
RNPGBE driver.

The series introduces:
- Basic ethtool driver and link information
- Link settings reporting for copper, fibre and backplane ports
- Copper autonegotiation, speed/duplex and MDI/MDI-X configuration
- Primary MAC address changes
- Hardware and per-queue statistics

These changes allow users to inspect and configure the adapter and
monitor interface and queue statistics.

Dong Yibo (5):
  net: rnpgbe: Add basic ethtool support
  net: rnpgbe: Report link settings through ethtool
  net: rnpgbe: Support copper link configuration
  net: rnpgbe: Allow changing the primary MAC address
  net: rnpgbe: Add hardware and queue statistics

Tested on N210 and N500 copper ports with firmware 1.1.8.0.

LLM assisted with development, testing and code review.

Dong Yibo (5):
  net: rnpgbe: Add basic ethtool support
  net: rnpgbe: Report link settings through ethtool
  net: rnpgbe: Support copper link configuration
  net: rnpgbe: Allow changing the primary MAC address
  net: rnpgbe: Add hardware and queue statistics

 drivers/net/ethernet/mucse/rnpgbe/Makefile    |   2 +
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h    |  24 ++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c   |  51 +++-
 .../ethernet/mucse/rnpgbe/rnpgbe_ethtool.c    | 242 ++++++++++++++++++
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h |   2 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c    |  28 +-
 .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c   |  17 +-
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c |  35 ++-
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h |  26 ++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_stats.c  | 217 ++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_stats.h  |  37 +++
 11 files changed, 672 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.h

-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support
  2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
@ 2026-11-06 11:25 ` Dong Yibo
  2026-11-07 17:45   ` Andrew Lunn
  2026-11-06 11:25 ` [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool Dong Yibo
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:25 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

Add get_drvinfo and get_link callbacks to report the driver name,
PCI bus address, firmware version and carrier state through ethtool.

Signed-off-by: Dong Yibo <dong100@mucse.com>
---
 drivers/net/ethernet/mucse/rnpgbe/Makefile    |  1 +
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h    |  3 +++
 .../ethernet/mucse/rnpgbe/rnpgbe_ethtool.c    | 25 +++++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c   |  1 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c |  4 ++-
 5 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c

diff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ethernet/mucse/rnpgbe/Makefile
index 17574cad392a..4d768324a82c 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/Makefile
+++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile
@@ -9,4 +9,5 @@ rnpgbe-objs := rnpgbe_main.o\
 	       rnpgbe_chip.o\
 	       rnpgbe_mbx.o\
 	       rnpgbe_mbx_fw.o\
+	       rnpgbe_ethtool.o\
 	       rnpgbe_lib.o
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 4e9776b28bfb..2fe76c95da7c 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -46,6 +46,7 @@ struct mucse_hw {
 	struct mucse_mbx_info mbx;
 	int port;
 	int speed;
+	u32 fw_version;
 	bool link;
 	u16 cycles_per_us;
 	u8 pfvfnum;
@@ -257,6 +258,8 @@ int rnpgbe_set_rx_mode(struct net_device *netdev,
 		       struct netdev_hw_addr_list *mc);
 void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);
 
+extern const struct ethtool_ops rnpgbe_ethtool_ops;
+
 /* Device IDs */
 #define PCI_VENDOR_ID_MUCSE               0x8848
 #define RNPGBE_DEVICE_ID_N500_QUAD_PORT   0x8308
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
new file mode 100644
index 000000000000..506233f62698
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 - 2026 Mucse Corporation. */
+
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+
+#include "rnpgbe.h"
+
+static void rnpgbe_get_drvinfo(struct net_device *netdev,
+			       struct ethtool_drvinfo *info)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	u32 version = mucse->hw.fw_version;
+
+	strscpy(info->driver, KBUILD_MODNAME);
+	strscpy(info->bus_info, pci_name(mucse->pdev));
+	snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u.%u",
+		 version >> 24, (version >> 16) & 0xff,
+		 (version >> 8) & 0xff, version & 0xff);
+}
+
+const struct ethtool_ops rnpgbe_ethtool_ops = {
+	.get_drvinfo = rnpgbe_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+};
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
index 708a7297233e..46981ef2542a 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -250,6 +250,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 	}
 
 	netdev->netdev_ops = &rnpgbe_netdev_ops;
+	netdev->ethtool_ops = &rnpgbe_ethtool_ops;
 	netdev->priv_flags |= IFF_UNICAST_FLT;
 	rnpgbe_sw_init(mucse);
 	err = rnpgbe_reset_hw(hw);
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
index c6ce9dbee3d0..4a1893792751 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
@@ -72,9 +72,11 @@ static int mucse_mbx_get_info(struct mucse_hw *hw)
 	int err;
 
 	err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
-	if (!err)
+	if (!err) {
 		hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0),
 					le16_to_cpu(reply.r.hw_info.pfnum));
+		hw->fw_version = le32_to_cpu(reply.r.hw_info.fw_version);
+	}
 
 	return err;
 }
-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool
  2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
  2026-11-06 11:25 ` [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support Dong Yibo
@ 2026-11-06 11:25 ` Dong Yibo
  2026-11-07 17:57   ` Andrew Lunn
  2026-11-10 16:55   ` netdev-bot+sashiko
  2026-11-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:25 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

Add get_link_ksettings support to report supported and advertised
link modes, autonegotiation, port type and PHY address. Report copper,
fibre and backplane link modes from firmware, with autonegotiation
support on copper ports.

These devices support autonegotiation only on TP ports. Configure TP
ports with autonegotiation enabled and fibre and backplane ports with
fixed 1 Gb/s full duplex.

Signed-off-by: Dong Yibo <dong100@mucse.com>
---
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h    |  13 ++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c   |  28 +++-
 .../ethernet/mucse/rnpgbe/rnpgbe_ethtool.c    | 126 ++++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c   |   7 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c |  29 ++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h |  26 ++++
 6 files changed, 227 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 2fe76c95da7c..83267d475257 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -39,11 +39,21 @@ enum {
 	mucse_fw_link_report_en,
 };
 
+struct mucse_link_config {
+	u32 supported;
+	u32 advertising;
+	u32 speed;
+	u16 status;
+	u8 phy_addr;
+	u8 duplex;
+};
+
 struct mucse_hw {
 	void __iomem *hw_addr;
 	void __iomem *ring_msix_base;
 	struct pci_dev *pdev;
 	struct mucse_mbx_info mbx;
+	struct mucse_link_config link_config;
 	int port;
 	int speed;
 	u32 fw_version;
@@ -51,6 +61,8 @@ struct mucse_hw {
 	u16 cycles_per_us;
 	u8 pfvfnum;
 	u8 duplex;
+	u8 mdix_ctrl;
+	u8 mdix;
 };
 
 struct rnpgbe_tx_desc {
@@ -249,6 +261,7 @@ struct mucse {
 int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);
 int rnpgbe_reset_hw(struct mucse_hw *hw);
 int rnpgbe_setup_default_link(struct mucse_hw *hw);
+int rnpgbe_init_link_config(struct mucse_hw *hw);
 int rnpgbe_send_notify(struct mucse_hw *hw,
 		       bool enable,
 		       int mode);
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
index dcc8233e024e..a6ab94bd1578 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
@@ -68,8 +68,32 @@ int rnpgbe_reset_hw(struct mucse_hw *hw)
  **/
 int rnpgbe_setup_default_link(struct mucse_hw *hw)
 {
-	return mucse_mbx_set_link(hw, RNPGBE_DEFAULT_ADV, true, 0, 0,
-				  ETH_TP_MDI_AUTO);
+	u32 advertising = hw->link_config.supported & RNPGBE_DEFAULT_ADV;
+	bool autoneg = hw->link_config.status & MUCSE_LINK_TP;
+	u8 duplex = DUPLEX_UNKNOWN;
+	u32 speed = SPEED_UNKNOWN;
+	int err;
+
+	if (!autoneg) {
+		advertising &= RNPGBE_FW_ADV_1000_FULL;
+		speed = SPEED_1000;
+		duplex = DUPLEX_FULL;
+	}
+
+	err = mucse_mbx_set_link(hw, advertising, autoneg,
+				 autoneg ? 0 : speed, autoneg ? 0 : duplex,
+				 ETH_TP_MDI_AUTO);
+	if (!err) {
+		hw->link_config.advertising = advertising;
+		hw->link_config.speed = speed;
+		hw->link_config.duplex = duplex;
+		if (autoneg)
+			hw->link_config.status |= MUCSE_LINK_AUTONEG;
+		else
+			hw->link_config.status &= ~MUCSE_LINK_AUTONEG;
+	}
+
+	return err;
 }
 
 /**
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
index 506233f62698..3ff01a15961e 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
@@ -2,9 +2,134 @@
 /* Copyright(c) 2022 - 2026 Mucse Corporation. */
 
 #include <linux/ethtool.h>
+#include <linux/linkmode.h>
 #include <linux/pci.h>
 
 #include "rnpgbe.h"
+#include "rnpgbe_mbx_fw.h"
+
+static const struct {
+	u32 fw_mode;
+	u32 ethtool_mode;
+} rnpgbe_tp_modes[] = {
+	{ RNPGBE_FW_ADV_10_HALF, ETHTOOL_LINK_MODE_10baseT_Half_BIT },
+	{ RNPGBE_FW_ADV_10_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT },
+	{ RNPGBE_FW_ADV_100_HALF, ETHTOOL_LINK_MODE_100baseT_Half_BIT },
+	{ RNPGBE_FW_ADV_100_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT },
+	{ RNPGBE_FW_ADV_1000_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT },
+};
+
+static void rnpgbe_link_modes(unsigned long *modes, u32 fw_modes)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rnpgbe_tp_modes); i++) {
+		if (fw_modes & rnpgbe_tp_modes[i].fw_mode)
+			linkmode_set_bit(rnpgbe_tp_modes[i].ethtool_mode,
+					 modes);
+	}
+}
+
+/**
+ * rnpgbe_init_link_config - Cache firmware link capabilities and configuration
+ * @hw: hardware information structure
+ *
+ * Query before port-up: repeated GET_LANE_STATUS commands can prevent
+ * firmware from completing forced 10 Mb/s link setup. Runtime link state
+ * comes from link events instead.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int rnpgbe_init_link_config(struct mucse_hw *hw)
+{
+	struct mucse_link_info info;
+	int err;
+
+	err = mucse_mbx_get_link(hw, &info);
+	if (err)
+		return err;
+
+	hw->link_config.supported = le32_to_cpu(info.supported);
+	hw->link_config.advertising = le32_to_cpu(info.advertising);
+	hw->link_config.status = le16_to_cpu(info.status);
+	hw->link_config.phy_addr = info.phy_addr;
+
+	return 0;
+}
+
+static int rnpgbe_get_link_ksettings(struct net_device *netdev,
+				     struct ethtool_link_ksettings *cmd)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	struct mucse_hw *hw = &mucse->hw;
+	u32 supported, advertising;
+	unsigned long flags;
+	u16 status;
+	int mode;
+
+	status = hw->link_config.status;
+	supported = hw->link_config.supported;
+	advertising = hw->link_config.advertising;
+	ethtool_link_ksettings_zero_link_mode(cmd, supported);
+	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
+	ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);
+	cmd->base.autoneg = AUTONEG_DISABLE;
+	cmd->base.speed = SPEED_UNKNOWN;
+	cmd->base.duplex = DUPLEX_UNKNOWN;
+	cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
+	spin_lock_irqsave(&mucse->link_lock, flags);
+	if (netif_carrier_ok(netdev) && hw->link) {
+		cmd->base.speed = hw->speed;
+		cmd->base.duplex = hw->duplex ?
+				   DUPLEX_FULL : DUPLEX_HALF;
+		if (status & MUCSE_LINK_TP)
+			cmd->base.eth_tp_mdix = hw->mdix;
+	}
+	spin_unlock_irqrestore(&mucse->link_lock, flags);
+
+	if (status & MUCSE_LINK_TP) {
+		rnpgbe_link_modes(cmd->link_modes.supported, supported);
+		rnpgbe_link_modes(cmd->link_modes.advertising, advertising);
+		ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
+		ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
+		ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+		if (status & MUCSE_LINK_AUTONEG) {
+			cmd->base.autoneg = AUTONEG_ENABLE;
+			ethtool_link_ksettings_add_link_mode(cmd, advertising,
+							     Autoneg);
+		}
+		cmd->base.port = PORT_TP;
+		cmd->base.phy_address = hw->link_config.phy_addr;
+		cmd->base.eth_tp_mdix_ctrl = hw->mdix_ctrl;
+	} else {
+		if (status & MUCSE_LINK_BACKPLANE) {
+			mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT;
+			ethtool_link_ksettings_add_link_mode(cmd, supported,
+							     Backplane);
+			ethtool_link_ksettings_add_link_mode(cmd, advertising,
+							     Backplane);
+			cmd->base.port = PORT_NONE;
+		} else {
+			mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT;
+			ethtool_link_ksettings_add_link_mode(cmd, supported,
+							     FIBRE);
+			ethtool_link_ksettings_add_link_mode(cmd, advertising,
+							     FIBRE);
+			cmd->base.port = PORT_FIBRE;
+		}
+		if (supported & RNPGBE_FW_ADV_1000_FULL)
+			linkmode_set_bit(mode, cmd->link_modes.supported);
+		if (advertising & RNPGBE_FW_ADV_1000_FULL)
+			linkmode_set_bit(mode, cmd->link_modes.advertising);
+	}
+
+	if (cmd->base.autoneg == AUTONEG_DISABLE) {
+		cmd->base.speed = hw->link_config.speed;
+		cmd->base.duplex = hw->link_config.duplex;
+	}
+
+	return 0;
+}
 
 static void rnpgbe_get_drvinfo(struct net_device *netdev,
 			       struct ethtool_drvinfo *info)
@@ -22,4 +147,5 @@ static void rnpgbe_get_drvinfo(struct net_device *netdev,
 const struct ethtool_ops rnpgbe_ethtool_ops = {
 	.get_drvinfo = rnpgbe_get_drvinfo,
 	.get_link = ethtool_op_get_link,
+	.get_link_ksettings = rnpgbe_get_link_ksettings,
 };
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
index 46981ef2542a..31871b244882 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -2,6 +2,7 @@
 /* Copyright(c) 2020 - 2025 Mucse Corporation. */
 
 #include <linux/pci.h>
+#include <linux/ethtool.h>
 #include <linux/skbuff.h>
 #include <net/rtnetlink.h>
 #include <linux/etherdevice.h>
@@ -169,6 +170,7 @@ 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;
+	mucse->hw.mdix_ctrl = ETH_TP_MDI_AUTO;
 
 	for (i = 0; i < RNPGBE_MAX_QUEUES; i++) {
 		u64_stats_init(&mucse->tx_stats[i].syncp);
@@ -258,6 +260,11 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 		dev_err(&pdev->dev, "Hw reset failed %d\n", err);
 		goto err_powerdown;
 	}
+	err = rnpgbe_init_link_config(hw);
+	if (err) {
+		dev_err(&pdev->dev, "Get link capabilities failed %d\n", err);
+		goto err_powerdown;
+	}
 	err = rnpgbe_setup_default_link(hw);
 	if (err) {
 		dev_err(&pdev->dev, "Setup link failed %d\n", err);
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
index 4a1893792751..469818b83d70 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
@@ -242,6 +242,33 @@ int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
 	return err;
 }
 
+/**
+ * mucse_mbx_get_link - Query firmware link settings and capabilities
+ * @hw: hardware information structure
+ * @info: firmware link information returned on success
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int mucse_mbx_get_link(struct mucse_hw *hw, struct mucse_link_info *info)
+{
+	union mbx_fw_cmd_req_u req = {
+		.r = {
+			.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN +
+					       sizeof(req.r.get_link)),
+			.opcode = cpu_to_le16(GET_LANE_STATUS),
+			.get_link.port = cpu_to_le32(hw->port),
+		},
+	};
+	union mbx_fw_cmd_reply_u reply = {};
+	int err;
+
+	err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
+	if (!err)
+		*info = reply.r.link_info;
+
+	return err;
+}
+
 /**
  * mucse_mbx_phyup - Request that firmware bring the PHY up or down
  * @hw: pointer to the HW structure
@@ -435,6 +462,8 @@ static void mucse_mbx_fw_req_handler(struct mucse_hw *hw,
 
 		hw->speed = le16_to_cpu(req->link_stat.st.speed);
 		hw->duplex = req->link_stat.st.flags & DUPLEX_BIT;
+		hw->mdix = FIELD_GET(ST_STATUS_MDIX_MASK,
+				     le16_to_cpu(req->link_stat.st.status));
 		rnpgbe_set_link(hw, mucse_link_is_up(hw, req));
 		/* update regs to notify link info is received */
 		mucse_update_link_status_reg(hw, req);
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
index 93ce0e796442..e99f6173e67d 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
@@ -15,6 +15,7 @@ enum MUCSE_FW_CMD {
 	GET_MAC_ADDRESS = 0x0602,
 	RESET_HW        = 0x0603,
 	LINK_CHANGE_EVT = 0x0608,
+	GET_LANE_STATUS = 0x0610,
 	LINK_REPORT_EN  = 0x0613,
 	PHY_LINK_SET    = 0x0630,
 	SET_PHY_UP      = 0x0800,
@@ -27,6 +28,25 @@ enum MUCSE_FW_CMD {
 #define RNPGBE_FW_ADV_10_HALF		BIT(10)
 #define RNPGBE_FW_ADV_100_HALF		BIT(11)
 
+struct mucse_link_info {
+	u8 reserved0[4];
+	__le16 status;
+	u8 phy_addr;
+	u8 reserved1;
+	__le32 speed;
+	__le32 reserved2[4];
+	__le32 supported;
+	__le32 phy_id;
+	__le32 advertising;
+} __packed;
+
+#define MUCSE_LINK_UP		BIT(0)
+#define MUCSE_LINK_FULL_DUPLEX	BIT(1)
+#define MUCSE_LINK_AUTONEG	BIT(2)
+#define MUCSE_LINK_TP		BIT(7)
+#define MUCSE_LINK_BACKPLANE	BIT(12)
+#define MUCSE_LINK_MDIX		GENMASK(14, 13)
+
 struct mucse_hw_info {
 	u8 link_stat;
 	u8 port_mask;
@@ -47,6 +67,7 @@ struct mucse_hw_info {
 } __packed;
 
 #define ST_STATUS_LLDP_STATUS_MASK        BIT(12)
+#define ST_STATUS_MDIX_MASK               GENMASK(11, 10)
 
 #define DUPLEX_BIT                        BIT(0)
 struct st_status {
@@ -87,6 +108,9 @@ struct mbx_fw_cmd_req {
 			__le32 nr_lane;
 			__le32 tp_mdix_ctrl;
 		} phy_link_set;
+		struct {
+			__le32 port;
+		} get_link;
 		struct {
 			/* LINK_REPORT_EN uses its own 16-bit payload layout:
 			 * status precedes port_mask.
@@ -123,6 +147,7 @@ struct mbx_fw_cmd_reply {
 			} addrs[4];
 		} mac_addr;
 		struct mucse_hw_info hw_info;
+		struct mucse_link_info link_info;
 	};
 } __packed;
 
@@ -156,6 +181,7 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
 			  u8 *mac_addr, int port);
 int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
 		       u32 speed, u32 duplex, u32 mdix_ctrl);
+int mucse_mbx_get_link(struct mucse_hw *hw, struct mucse_link_info *info);
 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);
-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration
  2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
  2026-11-06 11:25 ` [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support Dong Yibo
  2026-11-06 11:25 ` [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool Dong Yibo
@ 2026-11-06 11:25 ` Dong Yibo
  2026-11-07 18:04   ` Andrew Lunn
  2026-11-10 16:55   ` netdev-bot+sashiko
  2026-11-06 11:25 ` [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address Dong Yibo
  2026-11-06 11:25 ` [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics Dong Yibo
  4 siblings, 2 replies; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:25 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

Add set_link_ksettings support for copper ports to configure
autonegotiation, advertised link modes, forced 10/100 Mb/s speed and
duplex, and automatic or manual MDI/MDI-X selection.

Signed-off-by: Dong Yibo <dong100@mucse.com>
---
 .../ethernet/mucse/rnpgbe/rnpgbe_ethtool.c    | 88 +++++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c |  2 +-
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
index 3ff01a15961e..cd3cca1474ec 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
@@ -131,6 +131,93 @@ static int rnpgbe_get_link_ksettings(struct net_device *netdev,
 	return 0;
 }
 
+static u32 rnpgbe_fw_link_modes(const unsigned long *modes)
+{
+	u32 fw_modes = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rnpgbe_tp_modes); i++) {
+		if (linkmode_test_bit(rnpgbe_tp_modes[i].ethtool_mode, modes))
+			fw_modes |= rnpgbe_tp_modes[i].fw_mode;
+	}
+
+	return fw_modes;
+}
+
+static int rnpgbe_set_link_ksettings(struct net_device *netdev,
+				     const struct ethtool_link_ksettings *cmd)
+{
+	struct ethtool_link_ksettings settings = {};
+	struct mucse *mucse = netdev_priv(netdev);
+	u8 mdix = cmd->base.eth_tp_mdix_ctrl;
+	u32 advertising, mode, supported;
+	bool autoneg;
+	int err;
+
+	if (cmd->base.autoneg != AUTONEG_ENABLE &&
+	    cmd->base.autoneg != AUTONEG_DISABLE)
+		return -EINVAL;
+	autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
+	if (!mdix)
+		mdix = mucse->hw.mdix_ctrl;
+	if (mdix != ETH_TP_MDI && mdix != ETH_TP_MDI_X &&
+	    mdix != ETH_TP_MDI_AUTO)
+		return -EINVAL;
+
+	err = rnpgbe_get_link_ksettings(netdev, &settings);
+	if (err)
+		return err;
+	if (settings.base.port != PORT_TP)
+		return -EOPNOTSUPP;
+
+	supported = rnpgbe_fw_link_modes(settings.link_modes.supported);
+	advertising = rnpgbe_fw_link_modes(settings.link_modes.advertising);
+	if (autoneg) {
+		if (!linkmode_subset(cmd->link_modes.advertising,
+				     settings.link_modes.supported))
+			return -EINVAL;
+		advertising = rnpgbe_fw_link_modes(cmd->link_modes.advertising);
+		if (!advertising)
+			return -EINVAL;
+	} else {
+		if (cmd->base.duplex != DUPLEX_FULL &&
+		    cmd->base.duplex != DUPLEX_HALF)
+			return -EINVAL;
+		switch (cmd->base.speed) {
+		case SPEED_10:
+			mode = cmd->base.duplex == DUPLEX_FULL ?
+				RNPGBE_FW_ADV_10_FULL : RNPGBE_FW_ADV_10_HALF;
+			break;
+		case SPEED_100:
+			mode = cmd->base.duplex == DUPLEX_FULL ?
+				RNPGBE_FW_ADV_100_FULL : RNPGBE_FW_ADV_100_HALF;
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (!(supported & mode))
+			return -EINVAL;
+	}
+
+	err = mucse_mbx_set_link(&mucse->hw, advertising, autoneg,
+				 autoneg ? 0 : cmd->base.speed,
+				 autoneg ? 0 : cmd->base.duplex, mdix);
+	if (!err) {
+		mucse->hw.mdix_ctrl = mdix;
+		mucse->hw.link_config.advertising = advertising;
+		mucse->hw.link_config.speed = autoneg ?
+			SPEED_UNKNOWN : cmd->base.speed;
+		mucse->hw.link_config.duplex = autoneg ?
+			DUPLEX_UNKNOWN : cmd->base.duplex;
+		if (autoneg)
+			mucse->hw.link_config.status |= MUCSE_LINK_AUTONEG;
+		else
+			mucse->hw.link_config.status &= ~MUCSE_LINK_AUTONEG;
+	}
+
+	return err;
+}
+
 static void rnpgbe_get_drvinfo(struct net_device *netdev,
 			       struct ethtool_drvinfo *info)
 {
@@ -148,4 +235,5 @@ const struct ethtool_ops rnpgbe_ethtool_ops = {
 	.get_drvinfo = rnpgbe_get_drvinfo,
 	.get_link = ethtool_op_get_link,
 	.get_link_ksettings = rnpgbe_get_link_ksettings,
+	.set_link_ksettings = rnpgbe_set_link_ksettings,
 };
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
index 469818b83d70..7af56fce2e22 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
@@ -236,7 +236,7 @@ int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
 
 	len = le16_to_cpu(req.r.datalen);
 	mutex_lock(&hw->mbx.lock);
-	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
+	err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);
 	mutex_unlock(&hw->mbx.lock);
 
 	return err;
-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address
  2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
                   ` (2 preceding siblings ...)
  2026-11-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
@ 2026-11-06 11:25 ` Dong Yibo
  2026-11-10 16:55   ` netdev-bot+sashiko
  2026-11-06 11:25 ` [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics Dong Yibo
  4 siblings, 1 reply; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:25 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

Add ndo_set_mac_address support to change the primary MAC address
while the interface is up or down. Validate the new address with the
standard Ethernet helpers, then update the primary receive address
filter and the netdev address.

Signed-off-by: Dong Yibo <dong100@mucse.com>
---
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h    |  1 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c   | 23 +++++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c   |  4 +++-
 3 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 83267d475257..8574377e3a22 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -269,6 +269,7 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
 int rnpgbe_set_rx_mode(struct net_device *netdev,
 		       struct netdev_hw_addr_list *uc,
 		       struct netdev_hw_addr_list *mc);
+int rnpgbe_set_mac_address(struct net_device *netdev, void *addr);
 void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);
 
 extern const struct ethtool_ops rnpgbe_ethtool_ops;
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
index a6ab94bd1578..7505c9f6c79d 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
@@ -232,6 +232,29 @@ static void rnpgbe_clear_rar(struct mucse_hw *hw, u32 index)
 	mucse_hw_wr32(hw, RNPGBE_RX_RAR_LOW(index), 0);
 }
 
+/**
+ * rnpgbe_set_mac_address - Change the interface's primary MAC address
+ * @netdev: network interface
+ * @addr: socket address containing the new MAC address
+ *
+ * Return: 0 on success, negative errno for an invalid address
+ */
+int rnpgbe_set_mac_address(struct net_device *netdev, void *addr)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	struct sockaddr *sa = addr;
+	int err;
+
+	err = eth_prepare_mac_addr_change(netdev, addr);
+	if (err)
+		return err;
+
+	rnpgbe_set_rar(&mucse->hw, 0, sa->sa_data);
+	eth_commit_mac_addr_change(netdev, addr);
+
+	return 0;
+}
+
 int rnpgbe_set_rx_mode(struct net_device *netdev,
 		       struct netdev_hw_addr_list *uc,
 		       struct netdev_hw_addr_list *mc)
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
index 31871b244882..09d499dc10ee 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -160,6 +160,8 @@ static const struct net_device_ops rnpgbe_netdev_ops = {
 	.ndo_stop		 = rnpgbe_close,
 	.ndo_start_xmit		 = rnpgbe_xmit_frame,
 	.ndo_set_rx_mode_async = rnpgbe_set_rx_mode,
+	.ndo_set_mac_address	 = rnpgbe_set_mac_address,
+	.ndo_validate_addr	 = eth_validate_addr,
 	.ndo_get_stats64	 = rnpgbe_get_stats64,
 };
 
@@ -253,7 +255,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 
 	netdev->netdev_ops = &rnpgbe_netdev_ops;
 	netdev->ethtool_ops = &rnpgbe_ethtool_ops;
-	netdev->priv_flags |= IFF_UNICAST_FLT;
+	netdev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
 	rnpgbe_sw_init(mucse);
 	err = rnpgbe_reset_hw(hw);
 	if (err) {
-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics
  2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
                   ` (3 preceding siblings ...)
  2026-11-06 11:25 ` [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address Dong Yibo
@ 2026-11-06 11:25 ` Dong Yibo
  2026-11-10 16:55   ` netdev-bot+sashiko
  4 siblings, 1 reply; 15+ messages in thread
From: Dong Yibo @ 2026-11-06 11:25 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, dong100, yaojun

Add hardware receive CRC-error and multicast counters to
ndo_get_stats64. Accumulate the non-clearing 32-bit counters from the
service task while the interface is open, take a final sample on close
and retain totals across interface down/up cycles.

Report per-queue RX and TX packet and byte counters through netdev queue
statistics, with RX counters limited to packets delivered to the
networking stack. Report RX page and skb allocation failures together as
rx-alloc-fail, and include inactive queues in device totals.

Expose per-queue TX DMA mapping failures through ethtool statistics.

Signed-off-by: Dong Yibo <dong100@mucse.com>
---
 drivers/net/ethernet/mucse/rnpgbe/Makefile    |   1 +
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h    |   7 +
 .../ethernet/mucse/rnpgbe/rnpgbe_ethtool.c    |   3 +
 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h |   2 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c    |  28 ++-
 .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c   |   5 +
 .../net/ethernet/mucse/rnpgbe/rnpgbe_stats.c  | 217 ++++++++++++++++++
 .../net/ethernet/mucse/rnpgbe/rnpgbe_stats.h  |  37 +++
 8 files changed, 296 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c
 create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.h

diff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ethernet/mucse/rnpgbe/Makefile
index 4d768324a82c..2b498a3f297a 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/Makefile
+++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile
@@ -10,4 +10,5 @@ rnpgbe-objs := rnpgbe_main.o\
 	       rnpgbe_mbx.o\
 	       rnpgbe_mbx_fw.o\
 	       rnpgbe_ethtool.o\
+	       rnpgbe_stats.o\
 	       rnpgbe_lib.o
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 8574377e3a22..da2d5d1db9b5 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -13,6 +13,7 @@
 #include <linux/workqueue.h>
 
 #include "rnpgbe_hw.h"
+#include "rnpgbe_stats.h"
 
 enum rnpgbe_boards {
 	board_n500,
@@ -119,8 +120,13 @@ struct mucse_tx_buffer {
 struct mucse_queue_stats {
 	u64 packets;
 	u64 bytes;
+	u64 delivered_packets;
+	u64 delivered_bytes;
 	u64 length_errors;
 	atomic64_t dropped;
+	atomic64_t page_alloc_failures;
+	atomic64_t skb_alloc_failures;
+	atomic64_t dma_map_failures;
 	struct u64_stats_sync syncp;
 } ____cacheline_aligned_in_smp;
 
@@ -231,6 +237,7 @@ struct mucse {
 	struct net_device *netdev;
 	struct pci_dev *pdev;
 	struct mucse_hw hw;
+	struct mucse_hw_stats hw_stats;
 	struct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES]
 		____cacheline_aligned_in_smp;
 	struct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES]
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
index cd3cca1474ec..b311d1a70d30 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
@@ -236,4 +236,7 @@ const struct ethtool_ops rnpgbe_ethtool_ops = {
 	.get_link = ethtool_op_get_link,
 	.get_link_ksettings = rnpgbe_get_link_ksettings,
 	.set_link_ksettings = rnpgbe_set_link_ksettings,
+	.get_sset_count = rnpgbe_get_sset_count,
+	.get_strings = rnpgbe_get_strings,
+	.get_ethtool_stats = rnpgbe_get_ethtool_stats,
 };
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
index 674d4d23c4b0..c717eb6d00b6 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
@@ -36,8 +36,10 @@
 #define RNPGBE_LINK_ST                 0x000c
 #define RNPGBE_DMA_AXI_EN              0x0010
 #define RNPGBE_TX_MIN_PKT_LEN          33
+#define RNPGBE_DMA_RX_MULTICAST(_n)    (0x4018 + 0x100 * (_n))
 
 #define MUCSE_ETH_OFF(_n)              (0x10000 + (_n))
+#define RNPGBE_ETH_RX_FCS_ERRORS       MUCSE_ETH_OFF(0x8954)
 #define RNPGBE_RX_RAR_LOW(_n)          MUCSE_ETH_OFF(0xa000 + 4 * (_n))
 #define RNPGBE_RX_RAR_HIGH(_n)         MUCSE_ETH_OFF(0xa400 + 4 * (_n))
 #define RNPGBE_RX_MCAST_HASH(_n)       MUCSE_ETH_OFF(0xac00 + 4 * (_n))
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
index b7b0c45a0f66..fe320c4f389b 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
@@ -4,6 +4,7 @@
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include <linux/iopoll.h>
+#include <linux/jiffies.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
 #include <net/netdev_queues.h>
@@ -201,8 +202,10 @@ static bool mucse_alloc_mapped_page(struct mucse_ring *rx_ring,
 	}
 
 	page = page_pool_dev_alloc_pages(rx_ring->page_pool);
-	if (unlikely(!page))
+	if (unlikely(!page)) {
+		atomic64_inc(&rx_ring->stats->page_alloc_failures);
 		return false;
+	}
 	dma = page_pool_get_dma_addr(page);
 
 	bi->dma = dma;
@@ -468,6 +471,7 @@ static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,
 			       struct mucse_ring *rx_ring,
 			       int budget)
 {
+	unsigned int delivered_bytes = 0, delivered_packets = 0;
 	unsigned int total_rx_bytes = 0, total_rx_packets = 0;
 	u16 cleaned_count = mucse_desc_unused_rx(rx_ring);
 	unsigned int total_rx_length_errors = 0;
@@ -571,6 +575,7 @@ static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,
 			skb = rnpgbe_build_skb(rx_buffer, size);
 
 		if (!skb) {
+			atomic64_inc(&rx_ring->stats->skb_alloc_failures);
 			cleaned_count++;
 
 			/* drop until eop if multiple descriptors */
@@ -615,6 +620,8 @@ static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,
 
 		/* probably a little skewed due to removing CRC */
 		total_rx_bytes += skb->len;
+		delivered_bytes += skb->len;
+		delivered_packets++;
 		rnpgbe_process_skb_fields(rx_ring, skb);
 		rx_desc->wb.cmd = 0;
 		napi_gro_receive(&q_vector->napi, skb);
@@ -626,6 +633,8 @@ static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,
 	u64_stats_update_begin(&rx_ring->stats->syncp);
 	rx_ring->stats->packets += total_rx_packets;
 	rx_ring->stats->bytes += total_rx_bytes;
+	rx_ring->stats->delivered_packets += delivered_packets;
+	rx_ring->stats->delivered_bytes += delivered_bytes;
 	rx_ring->stats->length_errors += total_rx_length_errors;
 	atomic64_add(total_rx_dropped, &rx_ring->stats->dropped);
 	u64_stats_update_end(&rx_ring->stats->syncp);
@@ -1456,7 +1465,7 @@ void rnpgbe_down(struct mucse *mucse)
 	int err;
 
 	spin_lock_irqsave(&mucse->link_lock, flags);
-	mucse->link_event_ready = false;
+	WRITE_ONCE(mucse->link_event_ready, false);
 	mucse->link_pending = false;
 	hw->link = false;
 	hw->speed = 0;
@@ -1491,6 +1500,7 @@ void rnpgbe_down(struct mucse *mucse)
 			 err);
 		dev_warn(&hw->pdev->dev, "Port is not truly down\n");
 	}
+	rnpgbe_update_stats(mucse);
 	rnpgbe_clean_all_rx_rings(mucse);
 }
 
@@ -1527,7 +1537,7 @@ int rnpgbe_up_complete(struct mucse *mucse)
 	hw->speed = 0;
 	hw->duplex = 0;
 	mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST);
-	mucse->link_event_ready = true;
+	WRITE_ONCE(mucse->link_event_ready, true);
 	spin_unlock_irqrestore(&mucse->link_lock, flags);
 	/* Firmware checks RNPGBE_LINK_ST (driver's last-received link state)
 	 * and only asserts LINK_CHANGE_EVT when it differs from the actual link
@@ -1541,6 +1551,7 @@ int rnpgbe_up_complete(struct mucse *mucse)
 		return err;
 	}
 
+	schedule_delayed_work(&mucse->serv_task, HZ);
 	return 0;
 }
 
@@ -1760,8 +1771,10 @@ static int rnpgbe_tx_map(struct mucse_ring *tx_ring,
 	dma_unmap_addr_set(tx_buffer, dma, 0);
 
 	for (;; frag_idx++) {
-		if (dma_mapping_error(tx_ring->dev, dma))
+		if (dma_mapping_error(tx_ring->dev, dma)) {
+			atomic64_inc(&tx_ring->stats->dma_map_failures);
 			goto err_unmap;
+		}
 
 		/* record length, and DMA address */
 		dma_unmap_len_set(tx_buffer, len, size);
@@ -1961,6 +1974,8 @@ void rnpgbe_get_stats64(struct net_device *netdev,
 		stats->rx_errors += length_errors;
 		stats->rx_length_errors += length_errors;
 	}
+
+	rnpgbe_get_hw_stats(mucse, stats);
 }
 
 static int mucse_alloc_page_pool(struct mucse_ring *rx_ring)
@@ -2259,4 +2274,9 @@ void rnpgbe_service_task(struct work_struct *work)
 	struct mucse *mucse = container_of(work, struct mucse, serv_task.work);
 
 	rnpgbe_process_link_subtask(mucse);
+	rnpgbe_update_stats(mucse);
+
+	/* Leave an immediate invocation queued by a link event unchanged. */
+	if (READ_ONCE(mucse->link_event_ready))
+		schedule_delayed_work(&mucse->serv_task, HZ);
 }
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
index 09d499dc10ee..47d4f65c9213 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -177,8 +177,11 @@ static void rnpgbe_sw_init(struct mucse *mucse)
 	for (i = 0; i < RNPGBE_MAX_QUEUES; i++) {
 		u64_stats_init(&mucse->tx_stats[i].syncp);
 		atomic64_set(&mucse->tx_stats[i].dropped, 0);
+		atomic64_set(&mucse->tx_stats[i].dma_map_failures, 0);
 		u64_stats_init(&mucse->rx_stats[i].syncp);
 		atomic64_set(&mucse->rx_stats[i].dropped, 0);
+		atomic64_set(&mucse->rx_stats[i].page_alloc_failures, 0);
+		atomic64_set(&mucse->rx_stats[i].skb_alloc_failures, 0);
 	}
 }
 
@@ -255,6 +258,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 
 	netdev->netdev_ops = &rnpgbe_netdev_ops;
 	netdev->ethtool_ops = &rnpgbe_ethtool_ops;
+	netdev->stat_ops = &rnpgbe_stat_ops;
 	netdev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
 	rnpgbe_sw_init(mucse);
 	err = rnpgbe_reset_hw(hw);
@@ -314,6 +318,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 	}
 
 	netif_carrier_off(netdev);
+	rnpgbe_init_stats(mucse);
 	err = register_netdev(netdev);
 	if (err)
 		goto err_remove_mbx;
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c
new file mode 100644
index 000000000000..595faa16b7d0
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 - 2026 Mucse Corporation. */
+
+#include <linux/ethtool.h>
+#include <net/netdev_queues.h>
+
+#include "rnpgbe.h"
+#include "rnpgbe_stats.h"
+
+struct rnpgbe_queue_stat {
+	char name[ETH_GSTRING_LEN];
+	unsigned int offset;
+};
+
+#define RNPGBE_QUEUE_STAT(_field) \
+	{ #_field, offsetof(struct mucse_queue_stats, _field) }
+
+static const struct rnpgbe_queue_stat rnpgbe_tx_queue_stats[] = {
+	RNPGBE_QUEUE_STAT(dma_map_failures),
+};
+
+static void rnpgbe_get_queue_stats_rx(struct net_device *netdev, int idx,
+				      struct netdev_queue_stats_rx *stats)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	const struct mucse_queue_stats *queue;
+	unsigned int start;
+
+	queue = &mucse->rx_stats[idx];
+	do {
+		start = u64_stats_fetch_begin(&queue->syncp);
+		stats->packets = queue->delivered_packets;
+		stats->bytes = queue->delivered_bytes;
+	} while (u64_stats_fetch_retry(&queue->syncp, start));
+	stats->alloc_fail = atomic64_read(&queue->page_alloc_failures) +
+			    atomic64_read(&queue->skb_alloc_failures);
+}
+
+static void rnpgbe_get_queue_stats_tx(struct net_device *netdev, int idx,
+				      struct netdev_queue_stats_tx *stats)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	const struct mucse_queue_stats *queue;
+	unsigned int start;
+
+	queue = &mucse->tx_stats[idx];
+	do {
+		start = u64_stats_fetch_begin(&queue->syncp);
+		stats->packets = queue->packets;
+		stats->bytes = queue->bytes;
+	} while (u64_stats_fetch_retry(&queue->syncp, start));
+}
+
+static void rnpgbe_get_base_stats(struct net_device *netdev,
+				  struct netdev_queue_stats_rx *rx,
+				  struct netdev_queue_stats_tx *tx)
+{
+	rx->packets = 0;
+	rx->bytes = 0;
+	rx->alloc_fail = 0;
+	tx->packets = 0;
+	tx->bytes = 0;
+
+	/* Queue counters persist across interface down/up. Include any queues
+	 * outside the active range when reporting device totals.
+	 */
+	netdev_stat_queue_sum(netdev, netdev->real_num_rx_queues,
+			      netdev->num_rx_queues, rx,
+			      netdev->real_num_tx_queues,
+			      netdev->num_tx_queues, tx);
+}
+
+const struct netdev_stat_ops rnpgbe_stat_ops = {
+	.get_queue_stats_rx = rnpgbe_get_queue_stats_rx,
+	.get_queue_stats_tx = rnpgbe_get_queue_stats_tx,
+	.get_base_stats = rnpgbe_get_base_stats,
+};
+
+static void rnpgbe_read_stats(struct mucse *mucse)
+{
+	struct mucse_hw_stats *stats = &mucse->hw_stats;
+	struct mucse_hw *hw = &mucse->hw;
+	u32 value;
+	int i;
+
+	/* These 32-bit counters do not clear on read. Unsigned differences
+	 * extend them across wraparound; hardware reset clears them.
+	 */
+	value = mucse_hw_rd32(hw, RNPGBE_ETH_RX_FCS_ERRORS);
+	stats->rx_crc_errors += (u32)(value - stats->rx_crc_errors_prev);
+	stats->rx_crc_errors_prev = value;
+
+	for (i = 0; i < mucse->num_rx_queues; i++) {
+		u32 index = mucse->rx_ring[i]->rnpgbe_queue_idx;
+
+		value = mucse_hw_rd32(hw, RNPGBE_DMA_RX_MULTICAST(index));
+		stats->multicast += (u32)(value - stats->multicast_prev[i]);
+		stats->multicast_prev[i] = value;
+	}
+}
+
+/**
+ * rnpgbe_update_stats - Accumulate the hardware counters
+ * @mucse: device private structure
+ */
+void rnpgbe_update_stats(struct mucse *mucse)
+{
+	spin_lock_bh(&mucse->hw_stats.lock);
+	rnpgbe_read_stats(mucse);
+	spin_unlock_bh(&mucse->hw_stats.lock);
+}
+
+/**
+ * rnpgbe_init_stats - Initialize counter snapshots after the probe reset
+ * @mucse: device private structure
+ */
+void rnpgbe_init_stats(struct mucse *mucse)
+{
+	struct mucse_hw_stats *stats = &mucse->hw_stats;
+	struct mucse_hw *hw = &mucse->hw;
+	int i;
+
+	spin_lock_init(&stats->lock);
+
+	stats->rx_crc_errors_prev = mucse_hw_rd32(hw, RNPGBE_ETH_RX_FCS_ERRORS);
+	for (i = 0; i < mucse->num_rx_queues; i++) {
+		u32 index = mucse->rx_ring[i]->rnpgbe_queue_idx;
+
+		stats->multicast_prev[i] =
+			mucse_hw_rd32(hw, RNPGBE_DMA_RX_MULTICAST(index));
+	}
+}
+
+/**
+ * rnpgbe_get_hw_stats - Add cached counters to the interface statistics
+ * @mucse: device private structure
+ * @stats: interface statistics to update
+ */
+void rnpgbe_get_hw_stats(struct mucse *mucse, struct rtnl_link_stats64 *stats)
+{
+	spin_lock_bh(&mucse->hw_stats.lock);
+	stats->rx_crc_errors += mucse->hw_stats.rx_crc_errors;
+	stats->rx_errors += mucse->hw_stats.rx_crc_errors;
+	stats->multicast += mucse->hw_stats.multicast;
+	spin_unlock_bh(&mucse->hw_stats.lock);
+}
+
+/**
+ * rnpgbe_get_sset_count - Return the number of queue counters
+ * @netdev: network interface
+ * @sset: requested string set
+ *
+ * Return: counter count, or -EOPNOTSUPP for other string sets
+ */
+int rnpgbe_get_sset_count(struct net_device *netdev, int sset)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+
+	if (sset != ETH_SS_STATS)
+		return -EOPNOTSUPP;
+
+	return mucse->num_tx_queues * ARRAY_SIZE(rnpgbe_tx_queue_stats);
+}
+
+/**
+ * rnpgbe_get_strings - Report the queue counter names
+ * @netdev: network interface
+ * @sset: requested string set
+ * @data: buffer for fixed-width counter names
+ */
+void rnpgbe_get_strings(struct net_device *netdev, u32 sset, u8 *data)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	int i, j;
+
+	if (sset != ETH_SS_STATS)
+		return;
+
+	for (i = 0; i < mucse->num_tx_queues; i++)
+		for (j = 0; j < ARRAY_SIZE(rnpgbe_tx_queue_stats); j++)
+			ethtool_sprintf(&data, "tx_queue_%u_%s", i,
+					rnpgbe_tx_queue_stats[j].name);
+}
+
+static void rnpgbe_get_queue_stats(const struct mucse_queue_stats *queue,
+				   const struct rnpgbe_queue_stat *table,
+				   unsigned int count, u64 *data)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		const void *counter;
+
+		counter = (const u8 *)queue + table[i].offset;
+		data[i] = atomic64_read(counter);
+	}
+}
+
+/**
+ * rnpgbe_get_ethtool_stats - Report the queue counter values
+ * @netdev: network interface
+ * @stats: ethtool statistics request
+ * @data: buffer for values in the order returned by rnpgbe_get_strings()
+ */
+void rnpgbe_get_ethtool_stats(struct net_device *netdev,
+			      struct ethtool_stats *stats, u64 *data)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	int i;
+
+	for (i = 0; i < mucse->num_tx_queues; i++) {
+		rnpgbe_get_queue_stats(&mucse->tx_stats[i],
+				       rnpgbe_tx_queue_stats,
+				       ARRAY_SIZE(rnpgbe_tx_queue_stats), data);
+		data += ARRAY_SIZE(rnpgbe_tx_queue_stats);
+	}
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.h
new file mode 100644
index 000000000000..43fa4265ce6e
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2022 - 2026 Mucse Corporation. */
+
+#ifndef _RNPGBE_STATS_H
+#define _RNPGBE_STATS_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "rnpgbe_hw.h"
+
+struct ethtool_stats;
+struct net_device;
+struct netdev_stat_ops;
+struct rtnl_link_stats64;
+struct mucse;
+
+struct mucse_hw_stats {
+	/* Protects the hardware snapshots and software totals. */
+	spinlock_t lock;
+	u64 rx_crc_errors;
+	u64 multicast;
+	u32 rx_crc_errors_prev;
+	u32 multicast_prev[RNPGBE_MAX_QUEUES];
+};
+
+extern const struct netdev_stat_ops rnpgbe_stat_ops;
+
+void rnpgbe_init_stats(struct mucse *mucse);
+void rnpgbe_update_stats(struct mucse *mucse);
+void rnpgbe_get_hw_stats(struct mucse *mucse, struct rtnl_link_stats64 *stats);
+int rnpgbe_get_sset_count(struct net_device *netdev, int sset);
+void rnpgbe_get_strings(struct net_device *netdev, u32 sset, u8 *data);
+void rnpgbe_get_ethtool_stats(struct net_device *netdev,
+			      struct ethtool_stats *stats, u64 *data);
+
+#endif /* _RNPGBE_STATS_H */
-- 
2.50.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support
  2026-11-06 11:25 ` [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support Dong Yibo
@ 2026-11-07 17:45   ` Andrew Lunn
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Lunn @ 2026-11-07 17:45 UTC (permalink / raw)
  To: Dong Yibo
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

On Fri, Nov 06, 2026 at 07:25:10PM +0800, Dong Yibo wrote:
> Add get_drvinfo and get_link callbacks to report the driver name,
> PCI bus address, firmware version and carrier state through ethtool.
> 
> Signed-off-by: Dong Yibo <dong100@mucse.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool
  2026-11-06 11:25 ` [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool Dong Yibo
@ 2026-11-07 17:57   ` Andrew Lunn
  2026-11-09  5:57     ` Yibo Dong
  2026-11-10 16:55   ` netdev-bot+sashiko
  1 sibling, 1 reply; 15+ messages in thread
From: Andrew Lunn @ 2026-11-07 17:57 UTC (permalink / raw)
  To: Dong Yibo
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

> These devices support autonegotiation only on TP ports. Configure TP
> ports with autonegotiation enabled and fibre and backplane ports with
> fixed 1 Gb/s full duplex.
>  int rnpgbe_setup_default_link(struct mucse_hw *hw)
>  {
> -	return mucse_mbx_set_link(hw, RNPGBE_DEFAULT_ADV, true, 0, 0,
> -				  ETH_TP_MDI_AUTO);
> +	u32 advertising = hw->link_config.supported & RNPGBE_DEFAULT_ADV;
> +	bool autoneg = hw->link_config.status & MUCSE_LINK_TP;
> +	u8 duplex = DUPLEX_UNKNOWN;
> +	u32 speed = SPEED_UNKNOWN;
> +	int err;
> +
> +	if (!autoneg) {
> +		advertising &= RNPGBE_FW_ADV_1000_FULL;
> +		speed = SPEED_1000;
> +		duplex = DUPLEX_FULL;
> +	}
> +
> +	err = mucse_mbx_set_link(hw, advertising, autoneg,
> +				 autoneg ? 0 : speed, autoneg ? 0 : duplex,
> +				 ETH_TP_MDI_AUTO);

This looks odd. If autoneg is off, you expect user space to indicate
how the MAC should be configured. 10, 100, 1G, full or half.

> +static int rnpgbe_get_link_ksettings(struct net_device *netdev,
> +				     struct ethtool_link_ksettings *cmd)
> +{
> +	struct mucse *mucse = netdev_priv(netdev);
> +	struct mucse_hw *hw = &mucse->hw;
> +	u32 supported, advertising;
> +	unsigned long flags;
> +	u16 status;
> +	int mode;
> +
> +	status = hw->link_config.status;
> +	supported = hw->link_config.supported;
> +	advertising = hw->link_config.advertising;
> +	ethtool_link_ksettings_zero_link_mode(cmd, supported);
> +	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
> +	ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);

Do you have access to the LP advertisement value? It is useful for debugging.

> +	} else {
> +		if (status & MUCSE_LINK_BACKPLANE) {
> +			mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT;
> +			ethtool_link_ksettings_add_link_mode(cmd, supported,
> +							     Backplane);
> +			ethtool_link_ksettings_add_link_mode(cmd, advertising,
> +							     Backplane);

Does it actually advertise Backplane?


> +			cmd->base.port = PORT_NONE;
> +		} else {
> +			mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT;
> +			ethtool_link_ksettings_add_link_mode(cmd, supported,
> +							     FIBRE);
> +			ethtool_link_ksettings_add_link_mode(cmd, advertising,
> +							     FIBRE);

Again, does it advertise fibre? What happens if the LP wants to do TP?

You probably want to look at phylib, it figure out the correct way to
to implement ksetting set/get.


    Andrew

---
pw-bot: cr

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration
  2026-11-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
@ 2026-11-07 18:04   ` Andrew Lunn
  2026-11-09  9:21     ` Yibo Dong
  2026-11-10 16:55   ` netdev-bot+sashiko
  1 sibling, 1 reply; 15+ messages in thread
From: Andrew Lunn @ 2026-11-07 18:04 UTC (permalink / raw)
  To: Dong Yibo
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

> @@ -236,7 +236,7 @@ int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
>  
>  	len = le16_to_cpu(req.r.datalen);
>  	mutex_lock(&hw->mbx.lock);
> -	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
> +	err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);
>  	mutex_unlock(&hw->mbx.lock);

This seems out of place.

     Andrew

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool
  2026-11-07 17:57   ` Andrew Lunn
@ 2026-11-09  5:57     ` Yibo Dong
  0 siblings, 0 replies; 15+ messages in thread
From: Yibo Dong @ 2026-11-09  5:57 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

On Sat, Nov 07, 2026 at 06:57:45PM +0100, Andrew Lunn wrote:
> > These devices support autonegotiation only on TP ports. Configure TP
> > ports with autonegotiation enabled and fibre and backplane ports with
> > fixed 1 Gb/s full duplex.
> >  int rnpgbe_setup_default_link(struct mucse_hw *hw)
> >  {
> > -	return mucse_mbx_set_link(hw, RNPGBE_DEFAULT_ADV, true, 0, 0,
> > -				  ETH_TP_MDI_AUTO);
> > +	u32 advertising = hw->link_config.supported & RNPGBE_DEFAULT_ADV;
> > +	bool autoneg = hw->link_config.status & MUCSE_LINK_TP;
> > +	u8 duplex = DUPLEX_UNKNOWN;
> > +	u32 speed = SPEED_UNKNOWN;
> > +	int err;
> > +
> > +	if (!autoneg) {
> > +		advertising &= RNPGBE_FW_ADV_1000_FULL;
> > +		speed = SPEED_1000;
> > +		duplex = DUPLEX_FULL;
> > +	}
> > +
> > +	err = mucse_mbx_set_link(hw, advertising, autoneg,
> > +				 autoneg ? 0 : speed, autoneg ? 0 : duplex,
> > +				 ETH_TP_MDI_AUTO);
> 
> This looks odd. If autoneg is off, you expect user space to indicate
> how the MAC should be configured. 10, 100, 1G, full or half.
> 
This function sets the initial configuration during probe. It does not
handle an ethtool configuration request.
For the supported fibre and backplane configurations, the link operates
at fixed 1 Gb/s full duplex without autonegotiation. TP ports default to
autonegotiation, and the subsequent patch adds support for user-selected
forced 10/100 Mb/s speed and duplex settings.
The port type is fixed by the device firmware. TP, fibre and backplane
are mutually exclusive configurations, and the driver cannot switch a
port between them.
> > +static int rnpgbe_get_link_ksettings(struct net_device *netdev,
> > +				     struct ethtool_link_ksettings *cmd)
> > +{
> > +	struct mucse *mucse = netdev_priv(netdev);
> > +	struct mucse_hw *hw = &mucse->hw;
> > +	u32 supported, advertising;
> > +	unsigned long flags;
> > +	u16 status;
> > +	int mode;
> > +
> > +	status = hw->link_config.status;
> > +	supported = hw->link_config.supported;
> > +	advertising = hw->link_config.advertising;
> > +	ethtool_link_ksettings_zero_link_mode(cmd, supported);
> > +	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
> > +	ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);
> 
> Do you have access to the LP advertisement value? It is useful for debugging.
> 
Firmware manages PHY configuration, autonegotiation and reading the link
partner advertisement. However, the mailbox response currently used by
this driver only exposes the local capabilities and advertisement.
> > +	} else {
> > +		if (status & MUCSE_LINK_BACKPLANE) {
> > +			mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT;
> > +			ethtool_link_ksettings_add_link_mode(cmd, supported,
> > +							     Backplane);
> > +			ethtool_link_ksettings_add_link_mode(cmd, advertising,
> > +							     Backplane);
> 
> Does it actually advertise Backplane?
> 
> 
> > +			cmd->base.port = PORT_NONE;
> > +		} else {
> > +			mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT;
> > +			ethtool_link_ksettings_add_link_mode(cmd, supported,
> > +							     FIBRE);
> > +			ethtool_link_ksettings_add_link_mode(cmd, advertising,
> > +							     FIBRE);
> 
> Again, does it advertise fibre? What happens if the LP wants to do TP?
> 
These bits were added based on the fixed port type, rather than an
advertisement reported by firmware. I agree that this does not establish
that they are actually advertised.
A fibre port cannot switch to TP based on the link partner's capabilities.
The firmware-selected port type remains fixed.
Maybe I should revise the reporting for the fixed non-TP configurations?
retain the port type and supported link mode in supported/base.port, Andrew
leave advertising empty since these configurations do not use autonegotiation.
> You probably want to look at phylib, it figure out the correct way to
> to implement ksetting set/get.
> 
The current design leaves PHY management with firmware, with the driver
using mailbox commands to configure and query the link.
I will use phylib as a reference for validating user settings
and reporting link capabilities and configuration, while keeping PHY
management in firmware.
I will correct the advertisement reporting for fixed non-TP ports.
> 
>     Andrew
> 
> ---
Thanks for your feedback.

> pw-bot: cr
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration
  2026-11-07 18:04   ` Andrew Lunn
@ 2026-11-09  9:21     ` Yibo Dong
  0 siblings, 0 replies; 15+ messages in thread
From: Yibo Dong @ 2026-11-09  9:21 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

On Sat, Nov 07, 2026 at 07:04:20PM +0100, Andrew Lunn wrote:
> > @@ -236,7 +236,7 @@ int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
> >  
> >  	len = le16_to_cpu(req.r.datalen);
> >  	mutex_lock(&hw->mbx.lock);
> > -	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
> > +	err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);
> >  	mutex_unlock(&hw->mbx.lock);
> 
> This seems out of place.
This is needed because the new ethtool setter can configure a running
interface while firmware link-event reporting is enabled. Commands and
events share the mailbox buffer, so a pending event could be overwritten
by a command.
The coalescing helper acknowledges the pending event under the hardware
mailbox lock without updating the link-state snapshot, allowing firmware
to report the current state again.
Would you prefer this change to be split into a separate preparatory
patch, or would explaining the dependency in the commit message be
sufficient?
> 
>      Andrew
> 
Thanks for your feedback.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool
  2026-11-06 11:25 ` [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool Dong Yibo
  2026-11-07 17:57   ` Andrew Lunn
@ 2026-11-10 16:55   ` netdev-bot+sashiko
  1 sibling, 0 replies; 15+ messages in thread
From: netdev-bot+sashiko @ 2026-11-10 16:55 UTC (permalink / raw)
  To: dong100
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] In mucse_mbx_fw_req_handler() (rnpgbe_mbx_fw.c:465-466), the
  patch takes the 2-bit firmware field FIELD_GET(ST_STATUS_MDIX_MASK /*…

--

net: rnpgbe: Report link settings through ethtool

This commit adds get_link_ksettings support to rnpgbe. At probe time it
queries firmware with GET_LANE_STATUS and reports the supported and
advertised link modes, autoneg, port type and PHY address from that
reply. It also caches the MDI-X state from LINK_CHANGE_EVT, and sets
the default link to autoneg on TP ports and to fixed 1000/full on fibre
and backplane ports.

> diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
> index 4a18937927514..469818b83d70f 100644
> --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
> +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c

[ ... ]

> @@ -435,6 +462,8 @@ static void mucse_mbx_fw_req_handler(struct mucse_hw *hw,
>  
>  		hw->speed = le16_to_cpu(req->link_stat.st.speed);
>  		hw->duplex = req->link_stat.st.flags & DUPLEX_BIT;
> +		hw->mdix = FIELD_GET(ST_STATUS_MDIX_MASK,
> +				     le16_to_cpu(req->link_stat.st.status));

[Severity: Low]
This stores the raw 2-bit firmware field (0..3) in hw->mdix. Is that
value guaranteed to use the ETH_TP_MDI_* encoding?

When the carrier is up on a TP port, rnpgbe_get_link_ksettings() copies
the value straight into the ethtool status field:

	if (status & MUCSE_LINK_TP)
		cmd->base.eth_tp_mdix = hw->mdix;

The ethtool uAPI only allows ETH_TP_MDI_INVALID, ETH_TP_MDI or
ETH_TP_MDI_X in eth_tp_mdix. ETH_TP_MDI_AUTO (3) is only valid for
control. Both linkinfo_fill_reply() and the legacy ioctl path pass the
value to userspace without checking it.

If firmware reports 3, userspace gets an invalid status. If firmware
reports 0 while the link is up, userspace sees "Unknown". If firmware
uses a different encoding, userspace sees the wrong MDI/MDI-X state.

The control path already sends ETH_TP_MDI_* values to firmware in
phy_link_set.tp_mdix_ctrl, so values 1 and 2 may well line up. Nothing
here documents or enforces that, though, and later patches in the
series don't change the parsing or the reporting code.

Could the firmware value be mapped to ETH_TP_MDI_* explicitly, with
anything unexpected reported as ETH_TP_MDI_INVALID?

The patch also adds a second MDI-X field definition, for the
GET_LANE_STATUS status word in rnpgbe_mbx_fw.h, and nothing uses it:

	#define MUCSE_LINK_MDIX		GENMASK(14, 13)

Neither this define nor ST_STATUS_MDIX_MASK documents its value
encoding. Is MUCSE_LINK_MDIX meant to be used, or can it be dropped?

>  		rnpgbe_set_link(hw, mucse_link_is_up(hw, req));
>  		/* update regs to notify link info is received */
>  		mucse_update_link_status_reg(hw, req);

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/FB722C2F25FE9005%2B20261106112420.43050-1-dong100%40mucse.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration
  2026-11-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
  2026-11-07 18:04   ` Andrew Lunn
@ 2026-11-10 16:55   ` netdev-bot+sashiko
  1 sibling, 0 replies; 15+ messages in thread
From: netdev-bot+sashiko @ 2026-11-10 16:55 UTC (permalink / raw)
  To: dong100
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 3

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] In the forced (autoneg disabled) branch of
  rnpgbe_set_link_ksettings(), `mode` is computed from…
- [Low] The commit message says only that set_link_ksettings support is
  added for copper ports.
- [Low] rnpgbe_set_link_ksettings() never checks the requested
  cmd->base.port or cmd->base.phy_address.
- [Low] When the requested autoneg is disabled,
  rnpgbe_set_link_ksettings() never reads or validates…

--

net: rnpgbe: Support copper link configuration

This adds a set_link_ksettings ethtool callback for copper ports in the
rnpgbe driver. It covers autonegotiation, advertised link modes, forced
10/100 Mb/s speed and duplex, and MDI/MDI-X selection. It also switches
mucse_mbx_set_link() to send PHY_LINK_SET through
mucse_write_mbx_coalesce_event().

> diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
> index 3ff01a15961e3..cd3cca1474ec1 100644
> --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
> +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_ethtool.c
> @@ -131,6 +131,93 @@ static int rnpgbe_get_link_ksettings(struct net_device *netdev,

[ ... ]

> +static int rnpgbe_set_link_ksettings(struct net_device *netdev,
> +				     const struct ethtool_link_ksettings *cmd)
> +{

[ ... ]

> +	err = rnpgbe_get_link_ksettings(netdev, &settings);
> +	if (err)
> +		return err;
> +	if (settings.base.port != PORT_TP)
> +		return -EOPNOTSUPP;

[Severity: Low]
This tests the port in the driver's own freshly fetched settings, not the
requested cmd->base.port. Should cmd->base.port and cmd->base.phy_address
also be checked here?

ethnl_set_linkinfo() in net/ethtool/linkinfo.c loads the current settings,
applies the user's changes, and passes the result to the driver:

    ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
    ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
                    &mod);

So "ethtool -s ethX port fibre" or "ethtool -s ethX phyad N" on a copper
port seems to return success. The requested port and PHY address are
silently dropped, and PHY_LINK_SET is still sent with the other fields. A
later get still reports PORT_TP and the old phy_addr.

For comparison, phy_ethtool_ksettings_set() rejects a phy_address mismatch
with -EINVAL.

> +
> +	supported = rnpgbe_fw_link_modes(settings.link_modes.supported);
> +	advertising = rnpgbe_fw_link_modes(settings.link_modes.advertising);
> +	if (autoneg) {

[ ... ]

> +	} else {
> +		if (cmd->base.duplex != DUPLEX_FULL &&
> +		    cmd->base.duplex != DUPLEX_HALF)
> +			return -EINVAL;
> +		switch (cmd->base.speed) {
> +		case SPEED_10:
> +			mode = cmd->base.duplex == DUPLEX_FULL ?
> +				RNPGBE_FW_ADV_10_FULL : RNPGBE_FW_ADV_10_HALF;
> +			break;
> +		case SPEED_100:
> +			mode = cmd->base.duplex == DUPLEX_FULL ?
> +				RNPGBE_FW_ADV_100_FULL : RNPGBE_FW_ADV_100_HALF;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		if (!(supported & mode))
> +			return -EINVAL;
> +	}

[Severity: Medium]
In the forced branch, mode is used only for the supported check and is
never assigned to advertising. Is it intended that advertising keeps the
old cached mask from settings.link_modes.advertising?

That mask can still contain RNPGBE_FW_ADV_1000_FULL and other modes. It is
sent to firmware as adv_speed_mask along with autoneg=0 and a forced speed
of 10 or 100.

The existing forced-mode caller narrows the mask to the forced mode before
sending PHY_LINK_SET:

rnpgbe_chip.c:rnpgbe_setup_default_link() {
    ...
	if (!autoneg) {
		advertising &= RNPGBE_FW_ADV_1000_FULL;
		speed = SPEED_1000;
		duplex = DUPLEX_FULL;
	}
    ...
}

The mucse_mbx_set_link() kernel-doc says only that firmware ignores speed
and duplex when autoneg is true. Nothing says adv_speed_mask is ignored
when autoneg is false.

For example, "ethtool -s ethX autoneg off speed 100 duplex full" on a port
that was autonegotiating with a 1000/100/10 advertisement would send the
whole stale mask. On success, that mask is also cached into
hw->link_config.advertising. A later get_link_ksettings then reports
1000baseT_Full as advertised while the link is forced to 100.

Should advertising be set to mode in this branch?

> +
> +	err = mucse_mbx_set_link(&mucse->hw, advertising, autoneg,
> +				 autoneg ? 0 : cmd->base.speed,
> +				 autoneg ? 0 : cmd->base.duplex, mdix);
> +	if (!err) {
> +		mucse->hw.mdix_ctrl = mdix;
> +		mucse->hw.link_config.advertising = advertising;

[Severity: Low]
When autoneg is disabled, cmd->link_modes.advertising is never read or
validated. The old cached mask is written back here and the call returns
success. Can a user-requested advertising mask be silently lost this way?

ethnl_update_linkmodes() applies ETHTOOL_A_LINKMODES_OURS to
ksettings->link_modes.advertising whatever the autoneg state is. So
"ethtool -s ethX advertise <mask>" on a port in forced mode returns 0, but
the new mask is thrown away.

A later "ethtool -s ethX autoneg on" builds cmd from
rnpgbe_get_link_ksettings(), which returns the old cached mask. The
requested advertisement therefore never takes effect.

Also, bits outside the supported set get -EINVAL in autoneg mode but are
silently accepted in forced mode.

> +		mucse->hw.link_config.speed = autoneg ?

[ ... ]

> diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
> index 469818b83d70f..7af56fce2e229 100644
> --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
> +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
> @@ -236,7 +236,7 @@ int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
>  
>  	len = le16_to_cpu(req.r.datalen);
>  	mutex_lock(&hw->mbx.lock);
> -	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
> +	err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);

[Severity: Low]
This isn't a bug, but the commit message only describes adding
set_link_ksettings. It doesn't mention this change to the mailbox
transport used for PHY_LINK_SET.

mucse_write_mbx_coalesce_event() goes through
mucse_write_mbx_pf_coalesce_event(), which calls mucse_ack_pending_event()
before writing the command:

    err = mucse_ack_pending_event(hw);
    if (err)
        goto release;

This acknowledges any pending firmware LINK_CHANGE_EVT without consuming
its snapshot. It also adds a new -EIO return path for when the firmware
request counter reads 0.

It also changes the behaviour of the existing probe-time caller,
rnpgbe_setup_default_link().

The change is probably needed. PHY_LINK_SET can now be issued at runtime
while link event reporting is enabled, which is the same case as
mucse_mbx_phyup() and mucse_mbx_link_report(); both already use the
coalescing variant.

Could the reason be stated in the commit message, or the change split into
its own patch?

>  	mutex_unlock(&hw->mbx.lock);
>  
>  	return err;

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/FB722C2F25FE9005%2B20261106112420.43050-1-dong100%40mucse.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address
  2026-11-06 11:25 ` [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address Dong Yibo
@ 2026-11-10 16:55   ` netdev-bot+sashiko
  0 siblings, 0 replies; 15+ messages in thread
From: netdev-bot+sashiko @ 2026-11-10 16:55 UTC (permalink / raw)
  To: dong100
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] The commit 'net: rnpgbe: Allow changing the primary MAC address'
  adds two entries to rnpgbe_netdev_ops in…

--

net: rnpgbe: Allow changing the primary MAC address

This commit adds ndo_set_mac_address support to the rnpgbe driver, so the
primary MAC address can be changed while the interface is up or down. The
new address is validated with eth_prepare_mac_addr_change(), written to
RAR 0, and then committed to the netdev.

> diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
> index 31871b244882d..09d499dc10eeb 100644
> --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
> +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
> @@ -160,6 +160,8 @@ static const struct net_device_ops rnpgbe_netdev_ops = {
>  	.ndo_stop		 = rnpgbe_close,
>  	.ndo_start_xmit		 = rnpgbe_xmit_frame,
>  	.ndo_set_rx_mode_async = rnpgbe_set_rx_mode,
>  	.ndo_set_mac_address	 = rnpgbe_set_mac_address,
> +	.ndo_validate_addr	 = eth_validate_addr,

[Severity: Low]
This isn't a bug, but should the commit message also mention the
ndo_validate_addr hook added to rnpgbe_netdev_ops?

The commit message only covers ndo_set_mac_address:

  Add ndo_set_mac_address support to change the primary MAC address
  while the interface is up or down. Validate the new address with the
  standard Ethernet helpers, then update the primary receive address
  filter and the netdev address.

The "Validate the new address" sentence refers to the
eth_prepare_mac_addr_change() call in rnpgbe_set_mac_address().

eth_validate_addr is a separate change. __dev_open() calls it on every
ifup, so opening the device can now fail with -EADDRNOTAVAIL if
netdev->dev_addr is not a valid unicast address. Before this patch, open
did no such check.

As far as I can tell this check cannot fire today. In
rnpgbe_add_adapter(), probe either sets a firmware MAC that has already
passed is_valid_ether_addr() in rnpgbe_get_permanent_mac(), or it falls
back to eth_hw_addr_random(). After probe, rnpgbe_set_mac_address()
rejects invalid addresses.

Could the commit message mention the open-time validation, or could the
hook move to its own patch?

>  	.ndo_get_stats64	 = rnpgbe_get_stats64,
>  };

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/FB722C2F25FE9005%2B20261106112420.43050-1-dong100%40mucse.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics
  2026-11-06 11:25 ` [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics Dong Yibo
@ 2026-11-10 16:55   ` netdev-bot+sashiko
  0 siblings, 0 replies; 15+ messages in thread
From: netdev-bot+sashiko @ 2026-11-10 16:55 UTC (permalink / raw)
  To: dong100
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	linux-kernel, netdev, yaojun

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] This patch makes the rtnl and qstats RX totals count different
  things, so after any RX drop they can never match again.
- [Medium] rnpgbe_read_stats()
  (drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c) adds (u32)(value -…
- [Low] In rnpgbe_get_strings()
  (drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c), the loop index is…

--

net: rnpgbe: Add hardware and queue statistics

This commit adds hardware RX CRC-error and multicast counters to
ndo_get_stats64. The service task accumulates them, and the totals are
kept across down/up cycles. It also reports per-queue RX/TX packet and
byte counters and rx-alloc-fail through netdev queue stats, and exposes
per-queue TX DMA mapping failures through ethtool statistics.

> diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c
> new file mode 100644
> index 0000000000000..595faa16b7d0d
> --- /dev/null
> +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_stats.c

[ ... ]

> +static void rnpgbe_get_queue_stats_rx(struct net_device *netdev, int idx,
> +				      struct netdev_queue_stats_rx *stats)
> +{
> +	struct mucse *mucse = netdev_priv(netdev);
> +	const struct mucse_queue_stats *queue;
> +	unsigned int start;
> +
> +	queue = &mucse->rx_stats[idx];
> +	do {
> +		start = u64_stats_fetch_begin(&queue->syncp);
> +		stats->packets = queue->delivered_packets;
> +		stats->bytes = queue->delivered_bytes;
> +	} while (u64_stats_fetch_retry(&queue->syncp, start));

[Severity: Medium]
After an RX drop, can the qstats rx-packets/rx-bytes totals ever match the
rtnl totals again?

rnpgbe_get_stats64() fills rx_packets/rx_bytes from rx_stats[i].packets
and rx_stats[i].bytes. rnpgbe_clean_rx_irq() also increments those for
packets the driver drops. One example is an skb build failure at EOP:

rnpgbe_clean_rx_irq() {
    ...
		if (!skb) {
			atomic64_inc(&rx_ring->stats->skb_alloc_failures);
			...
			if (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)) {
				total_rx_bytes += size;
				total_rx_packets++;
				total_rx_dropped++;
    ...
}

Resource drops at EOP (drop_bytes) and rnpgbe_cleanup_headers() drops with
size >= ETH_HLEN are counted the same way. delivered_packets and
delivered_bytes are only incremented just before napi_gro_receive().
rnpgbe_get_base_stats() also sums the inactive queues through this
callback.

The counters persist across down/up. So after a single drop, qstats stays
lower than rtnl for the life of the device.

pkt_byte_sum() in tools/testing/selftests/drivers/net/stats.py requires
the two to be equal:

        qstat = get_qstat(cfg)
        if stat_cmp(rtstat, qstat) > 0:
            raise KsftFailEx("Qstats are lower, fetched later")

Before this patch the device had no stat_ops, so this test was skipped.
With this patch the test runs, and it fails after any such drop, even on
an idle link.

Could both interfaces report the same values? Alternatively, could the
dropped packets be reported through matching qstats fields so the device
totals agree?

> +	stats->alloc_fail = atomic64_read(&queue->page_alloc_failures) +
> +			    atomic64_read(&queue->skb_alloc_failures);
> +}

[ ... ]

> +static void rnpgbe_read_stats(struct mucse *mucse)
> +{
> +	struct mucse_hw_stats *stats = &mucse->hw_stats;
> +	struct mucse_hw *hw = &mucse->hw;
> +	u32 value;
> +	int i;
> +
> +	/* These 32-bit counters do not clear on read. Unsigned differences
> +	 * extend them across wraparound; hardware reset clears them.
> +	 */
> +	value = mucse_hw_rd32(hw, RNPGBE_ETH_RX_FCS_ERRORS);
> +	stats->rx_crc_errors += (u32)(value - stats->rx_crc_errors_prev);
> +	stats->rx_crc_errors_prev = value;

[Severity: Medium]
What happens here if the counter goes backwards, or if the read returns
0xFFFFFFFF?

The comment says a hardware reset clears these counters. However, the
prev snapshots are only taken from known hardware state once, in
rnpgbe_init_stats() at probe time.

The driver has no pci_error_handlers and no reset_prepare/reset_done
hooks. A runtime reset can therefore clear the counters while the driver
is still bound. Examples are a PCI function reset through sysfs, or AER
recovery of the parent bus. readl() can also return all-ones during a
PCIe error or link-down window.

In either case (u32)(value - prev) wraps, and about 2^32 - prev bogus
events are added to rx_crc_errors, rx_errors and multicast. The totals
are kept across down/up and only reset when the driver is reloaded. One
bad sample from rnpgbe_service_task() or rnpgbe_down() would inflate the
ndo_get_stats64 output until then.

The driver already notes in mucse_check_for_msg_pf() that chip registers
go back to 0 after a reset:

	/* chip's register is reset to 0 when rc send reset
	 * mbx command. Return -EIO if in this state, others
	 * fw == hw->mbx.fw_req means no new msg.

Should rnpgbe_read_stats() reject all-ones reads, and take a new snapshot
when it sees that a counter has been reset?

> +
> +	for (i = 0; i < mucse->num_rx_queues; i++) {
> +		u32 index = mucse->rx_ring[i]->rnpgbe_queue_idx;
> +
> +		value = mucse_hw_rd32(hw, RNPGBE_DMA_RX_MULTICAST(index));
> +		stats->multicast += (u32)(value - stats->multicast_prev[i]);
> +		stats->multicast_prev[i] = value;

The same question applies to the per-ring multicast counters here.

> +	}
> +}

[ ... ]

> +void rnpgbe_get_strings(struct net_device *netdev, u32 sset, u8 *data)
> +{
> +	struct mucse *mucse = netdev_priv(netdev);
> +	int i, j;
> +
> +	if (sset != ETH_SS_STATS)
> +		return;
> +
> +	for (i = 0; i < mucse->num_tx_queues; i++)
> +		for (j = 0; j < ARRAY_SIZE(rnpgbe_tx_queue_stats); j++)
> +			ethtool_sprintf(&data, "tx_queue_%u_%s", i,
> +					rnpgbe_tx_queue_stats[j].name);

[Severity: Low]
This isn't a bug, but i is a signed int and it is passed to a %u format
in ethtool_sprintf(). The output is correct because i is never negative,
but -Wformat-signedness would flag it. Declaring i as unsigned int, or
using %d, would make the type and format match.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/FB722C2F25FE9005%2B20261106112420.43050-1-dong100%40mucse.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-11-10 16:55 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 11:24 [PATCH net-next 0/5] net: rnpgbe: Add basic management and statistics Dong Yibo
2026-11-06 11:25 ` [PATCH net-next 1/5] net: rnpgbe: Add basic ethtool support Dong Yibo
2026-11-07 17:45   ` Andrew Lunn
2026-11-06 11:25 ` [PATCH net-next 2/5] net: rnpgbe: Report link settings through ethtool Dong Yibo
2026-11-07 17:57   ` Andrew Lunn
2026-11-09  5:57     ` Yibo Dong
2026-11-10 16:55   ` netdev-bot+sashiko
2026-11-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
2026-11-07 18:04   ` Andrew Lunn
2026-11-09  9:21     ` Yibo Dong
2026-11-10 16:55   ` netdev-bot+sashiko
2026-11-06 11:25 ` [PATCH net-next 4/5] net: rnpgbe: Allow changing the primary MAC address Dong Yibo
2026-11-10 16:55   ` netdev-bot+sashiko
2026-11-06 11:25 ` [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics Dong Yibo
2026-11-10 16:55   ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®