[PATCH net-next 6/7] net: xilinx: axienet: Dispatch statistics through axienet_config ops

From: Suraj Gupta

Date: Thu Jul 23 2026 - 08:41:37 EST


The ndo_get_stats64() and ethtool statistics callbacks read the 1G MAC
hardware counters directly. Supporting a second MAC type this way would
require an "if (mactype == ...)" dispatch branch in each of the eight
statistics callbacks.

Add statistics operation function pointers to struct axienet_config and
route get_stats64, get_ethtool_stats, get_strings, get_sset_count,
get_pause_stats, get_eth_mac_stats, get_eth_ctrl_stats and
get_rmon_stats through them. Factor the existing 1G counter reads into
axienet_1g_*() helpers and populate axienet_1g_config with them.

A NULL op means the MAC does not expose that statistics group, so a MAC
without control-frame counters simply leaves get_eth_ctrl_stats unset
instead of open-coding the special case in the callback.

No functional change intended.

Signed-off-by: Suraj Gupta <suraj.gupta2@xxxxxxx>
---
drivers/net/ethernet/xilinx/xilinx_axienet.h | 23 +++
.../net/ethernet/xilinx/xilinx_axienet_main.c | 171 +++++++++++++-----
2 files changed, 146 insertions(+), 48 deletions(-)

diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index a1d15b4797ce..f286c428343f 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -658,6 +658,15 @@ struct axienet_local {
* @phylink_set_caps: Callback to set phylink MAC capabilities
* @pcs_ops: phylink PCS operations for this MAC, or NULL if unused
* @stats_update: Callback to latch/accumulate the periodic MAC counters
+ * @get_stats64: Callback to read MAC counters into rtnl_link_stats64
+ * @get_ethtool_stats: Callback to read MAC counters for ethtool -S
+ * @get_strings: Callback to fill the ethtool -S statistic name strings
+ * @get_sset_count: Callback returning the number of ethtool -S statistics
+ * @get_pause_stats: Callback to read MAC pause-frame counters
+ * @get_eth_mac_stats: Callback to read IEEE 802.3 MAC counters
+ * @get_eth_ctrl_stats: Callback to read MAC-control frame counters, or NULL
+ * if the MAC exposes no control-frame counters
+ * @get_rmon_stats: Callback to read RMON counters and histogram ranges
*/
struct axienet_config {
bool mdio;
@@ -683,6 +692,20 @@ struct axienet_config {
struct phylink_config *config);
const struct phylink_pcs_ops *pcs_ops;
void (*stats_update)(struct axienet_local *lp);
+ void (*get_stats64)(struct axienet_local *lp,
+ struct rtnl_link_stats64 *stats);
+ void (*get_ethtool_stats)(struct axienet_local *lp, u64 *data);
+ void (*get_strings)(u8 *data);
+ int (*get_sset_count)(void);
+ void (*get_pause_stats)(struct axienet_local *lp,
+ struct ethtool_pause_stats *pause_stats);
+ void (*get_eth_mac_stats)(struct axienet_local *lp,
+ struct ethtool_eth_mac_stats *mac_stats);
+ void (*get_eth_ctrl_stats)(struct axienet_local *lp,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+ void (*get_rmon_stats)(struct axienet_local *lp,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
};

static inline struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 19fe2fd81f3a..198de353372a 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -1874,29 +1874,11 @@ static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phylink_mii_ioctl(lp->phylink, rq, cmd);
}

-static void
-axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+static void axienet_1g_get_stats64(struct axienet_local *lp,
+ struct rtnl_link_stats64 *stats)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;

- netdev_stats_to_stats64(stats, &dev->stats);
-
- do {
- start = u64_stats_fetch_begin(&lp->rx_stat_sync);
- stats->rx_packets = u64_stats_read(&lp->rx_packets);
- stats->rx_bytes = u64_stats_read(&lp->rx_bytes);
- } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start));
-
- do {
- start = u64_stats_fetch_begin(&lp->tx_stat_sync);
- stats->tx_packets = u64_stats_read(&lp->tx_packets);
- stats->tx_bytes = u64_stats_read(&lp->tx_bytes);
- } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start));
-
- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
stats->rx_length_errors =
@@ -1924,6 +1906,33 @@ axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}

+static void
+axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+ unsigned int start;
+
+ netdev_stats_to_stats64(stats, &dev->stats);
+
+ do {
+ start = u64_stats_fetch_begin(&lp->rx_stat_sync);
+ stats->rx_packets = u64_stats_read(&lp->rx_packets);
+ stats->rx_bytes = u64_stats_read(&lp->rx_bytes);
+ } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start));
+
+ do {
+ start = u64_stats_fetch_begin(&lp->tx_stat_sync);
+ stats->tx_packets = u64_stats_read(&lp->tx_packets);
+ stats->tx_bytes = u64_stats_read(&lp->tx_bytes);
+ } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start));
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_stats64)
+ lp->axienet_config->get_stats64(lp, stats);
+}
+
static const struct net_device_ops axienet_netdev_ops = {
.ndo_open = axienet_open,
.ndo_stop = axienet_stop,
@@ -2356,11 +2365,8 @@ static int axienet_ethtools_nway_reset(struct net_device *dev)
return phylink_ethtool_nway_reset(lp->phylink);
}

-static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats,
- u64 *data)
+static void axienet_1g_get_ethtool_stats(struct axienet_local *lp, u64 *data)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;

do {
@@ -2377,6 +2383,17 @@ static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}

+static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_ethtool_stats)
+ lp->axienet_config->get_ethtool_stats(lp, data);
+}
+
static const char axienet_ethtool_stats_strings[][ETH_GSTRING_LEN] = {
"Received bytes",
"Transmitted bytes",
@@ -2389,12 +2406,26 @@ static const char axienet_ethtool_stats_strings[][ETH_GSTRING_LEN] = {
"User Defined Counter 2",
};

+static void axienet_1g_get_strings(u8 *data)
+{
+ memcpy(data, axienet_ethtool_stats_strings,
+ sizeof(axienet_ethtool_stats_strings));
+}
+
+static int axienet_1g_get_sset_count(void)
+{
+ return ARRAY_SIZE(axienet_ethtool_stats_strings);
+}
+
static void axienet_ethtools_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
+ struct axienet_local *lp = netdev_priv(dev);
+
switch (stringset) {
case ETH_SS_STATS:
- memcpy(data, axienet_ethtool_stats_strings,
- sizeof(axienet_ethtool_stats_strings));
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_strings)
+ lp->axienet_config->get_strings(data);
break;
}
}
@@ -2405,24 +2436,20 @@ static int axienet_ethtools_get_sset_count(struct net_device *dev, int sset)

switch (sset) {
case ETH_SS_STATS:
- if (lp->features & XAE_FEATURE_STATS)
- return ARRAY_SIZE(axienet_ethtool_stats_strings);
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_sset_count)
+ return lp->axienet_config->get_sset_count();
fallthrough;
default:
return -EOPNOTSUPP;
}
}

-static void
-axienet_ethtools_get_pause_stats(struct net_device *dev,
- struct ethtool_pause_stats *pause_stats)
+static void axienet_1g_get_pause_stats(struct axienet_local *lp,
+ struct ethtool_pause_stats *pause_stats)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;

- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
pause_stats->tx_pause_frames =
@@ -2433,15 +2460,23 @@ axienet_ethtools_get_pause_stats(struct net_device *dev,
}

static void
-axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
- struct ethtool_eth_mac_stats *mac_stats)
+axienet_ethtools_get_pause_stats(struct net_device *dev,
+ struct ethtool_pause_stats *pause_stats)
{
struct axienet_local *lp = netdev_priv(dev);
- unsigned int start;

if (!(lp->features & XAE_FEATURE_STATS))
return;

+ if (lp->axienet_config->get_pause_stats)
+ lp->axienet_config->get_pause_stats(lp, pause_stats);
+}
+
+static void axienet_1g_get_eth_mac_stats(struct axienet_local *lp,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ unsigned int start;
+
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
mac_stats->FramesTransmittedOK =
@@ -2478,15 +2513,24 @@ axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
}

static void
-axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
+axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
+ struct ethtool_eth_mac_stats *mac_stats)
{
struct axienet_local *lp = netdev_priv(dev);
- unsigned int start;

if (!(lp->features & XAE_FEATURE_STATS))
return;

+ if (lp->axienet_config->get_eth_mac_stats)
+ lp->axienet_config->get_eth_mac_stats(lp, mac_stats);
+}
+
+static void
+axienet_1g_get_eth_ctrl_stats(struct axienet_local *lp,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ unsigned int start;
+
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
ctrl_stats->MACControlFramesTransmitted =
@@ -2498,6 +2542,19 @@ axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}

+static void
+axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_eth_ctrl_stats)
+ lp->axienet_config->get_eth_ctrl_stats(lp, ctrl_stats);
+}
+
const struct ethtool_rmon_hist_range axienet_rmon_ranges[] = {
{ 64, 64 },
{ 65, 127 },
@@ -2510,16 +2567,12 @@ const struct ethtool_rmon_hist_range axienet_rmon_ranges[] = {
};

static void
-axienet_ethtool_get_rmon_stats(struct net_device *dev,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
+axienet_1g_get_rmon_stats(struct axienet_local *lp,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;

- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
rmon_stats->undersize_pkts =
@@ -2563,6 +2616,20 @@ axienet_ethtool_get_rmon_stats(struct net_device *dev,
*ranges = axienet_rmon_ranges;
}

+static void
+axienet_ethtool_get_rmon_stats(struct net_device *dev,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_rmon_stats)
+ lp->axienet_config->get_rmon_stats(lp, rmon_stats, ranges);
+}
+
static const struct ethtool_ops axienet_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USECS |
@@ -2914,6 +2981,14 @@ static const struct axienet_config axienet_1g_config = {
.phylink_set_caps = axienet_1g_phylink_set_caps,
.pcs_ops = &axienet_pcs_ops,
.stats_update = axienet_1g_stats_update,
+ .get_stats64 = axienet_1g_get_stats64,
+ .get_ethtool_stats = axienet_1g_get_ethtool_stats,
+ .get_strings = axienet_1g_get_strings,
+ .get_sset_count = axienet_1g_get_sset_count,
+ .get_pause_stats = axienet_1g_get_pause_stats,
+ .get_eth_mac_stats = axienet_1g_get_eth_mac_stats,
+ .get_eth_ctrl_stats = axienet_1g_get_eth_ctrl_stats,
+ .get_rmon_stats = axienet_1g_get_rmon_stats,
};

/* Match table for of_platform binding */
--
2.25.1