Each user port of the NETC switch supports 802.3 basic and mandatory
managed objects statistic counters and IETF Management Information
Database (MIB) package (RFC2665) and Remote Network Monitoring (RMON)
counters. And all of these counters are 64-bit registers. In addition,
some user ports support preemption, so these ports have two MACs, MAC
0 is the express MAC (eMAC), MAC 1 is the preemptible MAC (pMAC). So
for ports that support preemption, the statistics are the sum of the
pMAC and eMAC statistics.

Note that the current switch driver does not support preemption, all
frames are sent and received via the eMAC by default. The statistics
read from the pMAC should be zero.

Signed-off-by: Wei Fang <[email protected]>
---
 drivers/net/dsa/netc/Makefile         |   2 +-
 drivers/net/dsa/netc/netc_ethtool.c   | 192 ++++++++++++++++++++++++++
 drivers/net/dsa/netc/netc_main.c      |   4 +
 drivers/net/dsa/netc/netc_switch.h    |  17 +++
 drivers/net/dsa/netc/netc_switch_hw.h | 153 ++++++++++++++++++++
 include/linux/fsl/netc_global.h       |   6 +
 6 files changed, 373 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/dsa/netc/netc_ethtool.c

diff --git a/drivers/net/dsa/netc/Makefile b/drivers/net/dsa/netc/Makefile
index 4a5767562574..f40b13c702e0 100644
--- a/drivers/net/dsa/netc/Makefile
+++ b/drivers/net/dsa/netc/Makefile
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_NET_DSA_NETC_SWITCH) += nxp-netc-switch.o
-nxp-netc-switch-objs := netc_main.o netc_platform.o
+nxp-netc-switch-objs := netc_main.o netc_platform.o netc_ethtool.o
diff --git a/drivers/net/dsa/netc/netc_ethtool.c 
b/drivers/net/dsa/netc/netc_ethtool.c
new file mode 100644
index 000000000000..8e5861543d30
--- /dev/null
+++ b/drivers/net/dsa/netc/netc_ethtool.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * NXP NETC switch driver
+ * Copyright 2025-2026 NXP
+ */
+
+#include <linux/ethtool_netlink.h>
+
+#include "netc_switch.h"
+
+static void netc_port_pause_stats(struct netc_port *np,
+                                 enum netc_port_mac mac,
+                                 struct ethtool_pause_stats *stats)
+{
+       if (mac == NETC_PORT_PMAC && !np->caps.pmac)
+               return;
+
+       stats->tx_pause_frames = netc_port_rd64(np, NETC_PM_TXPF(mac));
+       stats->rx_pause_frames = netc_port_rd64(np, NETC_PM_RXPF(mac));
+}
+
+void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
+                              struct ethtool_pause_stats *pause_stats)
+{
+       struct netc_port *np = NETC_PORT(ds, port);
+       struct net_device *ndev;
+
+       switch (pause_stats->src) {
+       case ETHTOOL_MAC_STATS_SRC_EMAC:
+               netc_port_pause_stats(np, NETC_PORT_EMAC, pause_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_PMAC:
+               netc_port_pause_stats(np, NETC_PORT_PMAC, pause_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+               ndev = dsa_to_port(ds, port)->user;
+               ethtool_aggregate_pause_stats(ndev, pause_stats);
+               break;
+       }
+}
+
+static const struct ethtool_rmon_hist_range netc_rmon_ranges[] = {
+       {   64,   64 },
+       {   65,  127 },
+       {  128,  255 },
+       {  256,  511 },
+       {  512, 1023 },
+       { 1024, 1522 },
+       { 1523, NETC_MAX_FRAME_LEN },
+       { }
+};
+
+static void netc_port_rmon_stats(struct netc_port *np,
+                                enum netc_port_mac mac,
+                                struct ethtool_rmon_stats *stats)
+{
+       if (mac == NETC_PORT_PMAC && !np->caps.pmac)
+               return;
+
+       stats->undersize_pkts = netc_port_rd64(np, NETC_PM_RUND(mac));
+       stats->oversize_pkts = netc_port_rd64(np, NETC_PM_ROVR(mac));
+       stats->fragments = netc_port_rd64(np, NETC_PM_RFRG(mac));
+       stats->jabbers = netc_port_rd64(np, NETC_PM_RJBR(mac));
+
+       stats->hist[0] = netc_port_rd64(np, NETC_PM_R64(mac));
+       stats->hist[1] = netc_port_rd64(np, NETC_PM_R127(mac));
+       stats->hist[2] = netc_port_rd64(np, NETC_PM_R255(mac));
+       stats->hist[3] = netc_port_rd64(np, NETC_PM_R511(mac));
+       stats->hist[4] = netc_port_rd64(np, NETC_PM_R1023(mac));
+       stats->hist[5] = netc_port_rd64(np, NETC_PM_R1522(mac));
+       stats->hist[6] = netc_port_rd64(np, NETC_PM_R1523X(mac));
+
+       stats->hist_tx[0] = netc_port_rd64(np, NETC_PM_T64(mac));
+       stats->hist_tx[1] = netc_port_rd64(np, NETC_PM_T127(mac));
+       stats->hist_tx[2] = netc_port_rd64(np, NETC_PM_T255(mac));
+       stats->hist_tx[3] = netc_port_rd64(np, NETC_PM_T511(mac));
+       stats->hist_tx[4] = netc_port_rd64(np, NETC_PM_T1023(mac));
+       stats->hist_tx[5] = netc_port_rd64(np, NETC_PM_T1522(mac));
+       stats->hist_tx[6] = netc_port_rd64(np, NETC_PM_T1523X(mac));
+}
+
+void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
+                             struct ethtool_rmon_stats *rmon_stats,
+                             const struct ethtool_rmon_hist_range **ranges)
+{
+       struct netc_port *np = NETC_PORT(ds, port);
+       struct net_device *ndev;
+
+       *ranges = netc_rmon_ranges;
+
+       switch (rmon_stats->src) {
+       case ETHTOOL_MAC_STATS_SRC_EMAC:
+               netc_port_rmon_stats(np, NETC_PORT_EMAC, rmon_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_PMAC:
+               netc_port_rmon_stats(np, NETC_PORT_PMAC, rmon_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+               ndev = dsa_to_port(ds, port)->user;
+               ethtool_aggregate_rmon_stats(ndev, rmon_stats);
+               break;
+       }
+}
+
+static void netc_port_ctrl_stats(struct netc_port *np,
+                                enum netc_port_mac mac,
+                                struct ethtool_eth_ctrl_stats *stats)
+{
+       if (mac == NETC_PORT_PMAC && !np->caps.pmac)
+               return;
+
+       stats->MACControlFramesTransmitted =
+               netc_port_rd64(np, NETC_PM_TCNP(mac));
+       stats->MACControlFramesReceived =
+               netc_port_rd64(np, NETC_PM_RCNP(mac));
+}
+
+void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+                                 struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+       struct netc_port *np = NETC_PORT(ds, port);
+       struct net_device *ndev;
+
+       switch (ctrl_stats->src) {
+       case ETHTOOL_MAC_STATS_SRC_EMAC:
+               netc_port_ctrl_stats(np, NETC_PORT_EMAC, ctrl_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_PMAC:
+               netc_port_ctrl_stats(np, NETC_PORT_PMAC, ctrl_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+               ndev = dsa_to_port(ds, port)->user;
+               ethtool_aggregate_ctrl_stats(ndev, ctrl_stats);
+               break;
+       }
+}
+
+static void netc_port_mac_stats(struct netc_port *np,
+                               enum netc_port_mac mac,
+                               struct ethtool_eth_mac_stats *stats)
+{
+       if (mac == NETC_PORT_PMAC && !np->caps.pmac)
+               return;
+
+       stats->FramesTransmittedOK = netc_port_rd64(np, NETC_PM_TFRM(mac));
+       stats->SingleCollisionFrames = netc_port_rd64(np, NETC_PM_TSCOL(mac));
+       stats->MultipleCollisionFrames =
+               netc_port_rd64(np, NETC_PM_TMCOL(mac));
+       stats->FramesReceivedOK = netc_port_rd64(np, NETC_PM_RFRM(mac));
+       stats->FrameCheckSequenceErrors =
+               netc_port_rd64(np, NETC_PM_RFCS(mac));
+       stats->AlignmentErrors = netc_port_rd64(np, NETC_PM_RALN(mac));
+       stats->OctetsTransmittedOK = netc_port_rd64(np, NETC_PM_TEOCT(mac));
+       stats->FramesWithDeferredXmissions =
+               netc_port_rd64(np, NETC_PM_TDFR(mac));
+       stats->LateCollisions = netc_port_rd64(np, NETC_PM_TLCOL(mac));
+       stats->FramesAbortedDueToXSColls =
+               netc_port_rd64(np, NETC_PM_TECOL(mac));
+       stats->FramesLostDueToIntMACXmitError =
+               netc_port_rd64(np, NETC_PM_TERR(mac));
+       stats->OctetsReceivedOK = netc_port_rd64(np, NETC_PM_REOCT(mac));
+       stats->FramesLostDueToIntMACRcvError =
+               netc_port_rd64(np, NETC_PM_RDRNTP(mac));
+       stats->MulticastFramesXmittedOK =
+               netc_port_rd64(np, NETC_PM_TMCA(mac));
+       stats->BroadcastFramesXmittedOK =
+               netc_port_rd64(np, NETC_PM_TBCA(mac));
+       stats->MulticastFramesReceivedOK =
+               netc_port_rd64(np, NETC_PM_RMCA(mac));
+       stats->BroadcastFramesReceivedOK =
+               netc_port_rd64(np, NETC_PM_RBCA(mac));
+}
+
+void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
+                                struct ethtool_eth_mac_stats *mac_stats)
+{
+       struct netc_port *np = NETC_PORT(ds, port);
+       struct net_device *ndev;
+
+       switch (mac_stats->src) {
+       case ETHTOOL_MAC_STATS_SRC_EMAC:
+               netc_port_mac_stats(np, NETC_PORT_EMAC, mac_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_PMAC:
+               netc_port_mac_stats(np, NETC_PORT_PMAC, mac_stats);
+               break;
+       case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+               ndev = dsa_to_port(ds, port)->user;
+               ethtool_aggregate_mac_stats(ndev, mac_stats);
+               break;
+       }
+}
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index 15dd8d89f66d..bb8e89bae758 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -1470,6 +1470,10 @@ static const struct dsa_switch_ops netc_switch_ops = {
        .port_mdb_add                   = netc_port_mdb_add,
        .port_mdb_del                   = netc_port_mdb_del,
        .port_set_host_flood            = netc_port_set_host_flood,
+       .get_pause_stats                = netc_port_get_pause_stats,
+       .get_rmon_stats                 = netc_port_get_rmon_stats,
+       .get_eth_ctrl_stats             = netc_port_get_eth_ctrl_stats,
+       .get_eth_mac_stats              = netc_port_get_eth_mac_stats,
 };
 
 static int netc_switch_probe(struct pci_dev *pdev,
diff --git a/drivers/net/dsa/netc/netc_switch.h 
b/drivers/net/dsa/netc/netc_switch.h
index 7ebffb136b2f..825601847c89 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -70,6 +70,11 @@ struct netc_port {
        struct ipft_entry_data *host_flood;
 };
 
+enum netc_port_mac {
+       NETC_PORT_EMAC = 0,
+       NETC_PORT_PMAC,
+};
+
 struct netc_switch_regs {
        void __iomem *base;
        void __iomem *port;
@@ -110,6 +115,7 @@ struct netc_switch {
 
 /* Write/Read registers of Switch Port (including pseudo MAC port) */
 #define netc_port_rd(p, o)             netc_read((p)->iobase + (o))
+#define netc_port_rd64(p, o)           netc_read64((p)->iobase + (o))
 #define netc_port_wr(p, o, v)          netc_write((p)->iobase + (o), v)
 
 /* Write/Read Switch global registers */
@@ -135,4 +141,15 @@ static inline void netc_del_fdb_entry(struct 
netc_fdb_entry *entry)
 
 int netc_switch_platform_probe(struct netc_switch *priv);
 
+/* ethtool APIs */
+void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
+                              struct ethtool_pause_stats *pause_stats);
+void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
+                             struct ethtool_rmon_stats *rmon_stats,
+                             const struct ethtool_rmon_hist_range **ranges);
+void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+                                 struct ethtool_eth_ctrl_stats *ctrl_stats);
+void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
+                                struct ethtool_eth_mac_stats *mac_stats);
+
 #endif
diff --git a/drivers/net/dsa/netc/netc_switch_hw.h 
b/drivers/net/dsa/netc/netc_switch_hw.h
index 7ef870fbba4d..9be859d20675 100644
--- a/drivers/net/dsa/netc/netc_switch_hw.h
+++ b/drivers/net/dsa/netc/netc_switch_hw.h
@@ -193,6 +193,159 @@ enum netc_stg_stage {
 #define   SSP_1G                       2
 #define  PM_IF_MODE_ENA                        BIT(15)
 
+/* Port MAC 0/1 Receive Ethernet Octets Counter */
+#define NETC_PM_REOCT(a)               (0x1100 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Octets Counter */
+#define NETC_PM_ROCT(a)                        (0x1108 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Alignment Error Counter Register */
+#define NETC_PM_RALN(a)                        (0x1110 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Valid Pause Frame Counter */
+#define NETC_PM_RXPF(a)                        (0x1118 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Counter */
+#define NETC_PM_RFRM(a)                        (0x1120 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
+#define NETC_PM_RFCS(a)                        (0x1128 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive VLAN Frame Counter */
+#define NETC_PM_RVLAN(a)               (0x1130 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Error Counter */
+#define NETC_PM_RERR(a)                        (0x1138 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Unicast Frame Counter */
+#define NETC_PM_RUCA(a)                        (0x1140 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Multicast Frame Counter */
+#define NETC_PM_RMCA(a)                        (0x1148 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Broadcast Frame Counter */
+#define NETC_PM_RBCA(a)                        (0x1150 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Dropped Packets Counter */
+#define NETC_PM_RDRP(a)                        (0x1158 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Packets Counter */
+#define NETC_PM_RPKT(a)                        (0x1160 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Undersized Packet Counter */
+#define NETC_PM_RUND(a)                        (0x1168 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 64-Octet Packet Counter */
+#define NETC_PM_R64(a)                 (0x1170 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 65 to 127-Octet Packet Counter */
+#define NETC_PM_R127(a)                        (0x1178 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 128 to 255-Octet Packet Counter */
+#define NETC_PM_R255(a)                        (0x1180 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 256 to 511-Octet Packet Counter */
+#define NETC_PM_R511(a)                        (0x1188 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 512 to 1023-Octet Packet Counter */
+#define NETC_PM_R1023(a)               (0x1190 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 1024 to 1522-Octet Packet Counter */
+#define NETC_PM_R1522(a)               (0x1198 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive 1523 to Max-Octet Packet Counter */
+#define NETC_PM_R1523X(a)              (0x11a0 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Oversized Packet Counter */
+#define NETC_PM_ROVR(a)                        (0x11a8 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Jabber Packet Counter */
+#define NETC_PM_RJBR(a)                        (0x11b0 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Fragment Packet Counter */
+#define NETC_PM_RFRG(a)                        (0x11b8 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Control Packet Counter */
+#define NETC_PM_RCNP(a)                        (0x11c0 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Dropped Not Truncated Packets Counter */
+#define NETC_PM_RDRNTP(a)              (0x11c8 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Ethernet Octets Counter */
+#define NETC_PM_TEOCT(a)               (0x1200 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Octets Counter */
+#define NETC_PM_TOCT(a)                        (0x1208 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Valid Pause Frame Counter */
+#define NETC_PM_TXPF(a)                        (0x1218 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Frame Counter */
+#define NETC_PM_TFRM(a)                        (0x1220 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */
+#define NETC_PM_TFCS(a)                        (0x1228 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit VLAN Frame Counter */
+#define NETC_PM_TVLAN(a)               (0x1230 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Frame Error Counter */
+#define NETC_PM_TERR(a)                        (0x1238 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Unicast Frame Counter */
+#define NETC_PM_TUCA(a)                        (0x1240 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Multicast Frame Counter */
+#define NETC_PM_TMCA(a)                        (0x1248 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Broadcast Frame Counter */
+#define NETC_PM_TBCA(a)                        (0x1250 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Packets Counter */
+#define NETC_PM_TPKT(a)                        (0x1260 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Undersized Packet Counter */
+#define NETC_PM_TUND(a)                        (0x1268 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 64-Octet Packet Counter */
+#define NETC_PM_T64(a)                 (0x1270 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 65 to 127-Octet Packet Counter */
+#define NETC_PM_T127(a)                        (0x1278 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 128 to 255-Octet Packet Counter */
+#define NETC_PM_T255(a)                        (0x1280 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 256 to 511-Octet Packet Counter */
+#define NETC_PM_T511(a)                        (0x1288 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 512 to 1023-Octet Packet Counter */
+#define NETC_PM_T1023(a)               (0x1290 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 1024 to 1522-Octet Packet Counter */
+#define NETC_PM_T1522(a)               (0x1298 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit 1523 to TX_MTU-Octet Packet Counter */
+#define NETC_PM_T1523X(a)              (0x12a0 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Control Packet Counter */
+#define NETC_PM_TCNP(a)                        (0x12c0 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Deferred Packet Counter */
+#define NETC_PM_TDFR(a)                        (0x12d0 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Multiple Collisions Counter */
+#define NETC_PM_TMCOL(a)               (0x12d8 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Single Collision */
+#define NETC_PM_TSCOL(a)               (0x12e0 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Late Collision Counter */
+#define NETC_PM_TLCOL(a)               (0x12e8 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Excessive Collisions Counter */
+#define NETC_PM_TECOL(a)               (0x12f0 + (a) * 0x400)
+
 #define NETC_PEMDIOCR                  0x1c00
 #define NETC_EMDIO_BASE                        NETC_PEMDIOCR
 
diff --git a/include/linux/fsl/netc_global.h b/include/linux/fsl/netc_global.h
index fdecca8c90f0..5b8ff528d369 100644
--- a/include/linux/fsl/netc_global.h
+++ b/include/linux/fsl/netc_global.h
@@ -5,6 +5,7 @@
 #define __NETC_GLOBAL_H
 
 #include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
 
 static inline u32 netc_read(void __iomem *reg)
 {
@@ -16,4 +17,9 @@ static inline void netc_write(void __iomem *reg, u32 val)
        iowrite32(val, reg);
 }
 
+static inline u64 netc_read64(void __iomem *reg)
+{
+       return ioread64(reg);
+}
+
 #endif
-- 
2.34.1


Reply via email to