* [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; 9+ 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] 9+ 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; 9+ 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] 9+ 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-06 11:25 ` [PATCH net-next 3/5] net: rnpgbe: Support copper link configuration Dong Yibo
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ 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] 9+ 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-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, 1 reply; 9+ 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] 9+ 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-06 11:25 ` [PATCH net-next 5/5] net: rnpgbe: Add hardware and queue statistics Dong Yibo
4 siblings, 0 replies; 9+ 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] 9+ 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
4 siblings, 0 replies; 9+ 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] 9+ 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; 9+ 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] 9+ 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
0 siblings, 0 replies; 9+ 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] 9+ 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
0 siblings, 0 replies; 9+ 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] 9+ messages in thread
end of thread, other threads:[~2026-11-07 18:04 UTC | newest]
Thread overview: 9+ 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-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-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
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®