xref: /linux/drivers/net/dsa/netc/netc_ethtool.c (revision beb0e54f3806cf01a24740b23ec01c4fab186b5c)
125049d8bSWei Fang // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
225049d8bSWei Fang /*
325049d8bSWei Fang  * NXP NETC switch driver
425049d8bSWei Fang  * Copyright 2025-2026 NXP
525049d8bSWei Fang  */
625049d8bSWei Fang 
725049d8bSWei Fang #include <linux/ethtool_netlink.h>
825049d8bSWei Fang 
925049d8bSWei Fang #include "netc_switch.h"
1025049d8bSWei Fang 
1125049d8bSWei Fang static const struct ethtool_rmon_hist_range netc_rmon_ranges[] = {
1225049d8bSWei Fang 	{   64,   64 },
1325049d8bSWei Fang 	{   65,  127 },
1425049d8bSWei Fang 	{  128,  255 },
1525049d8bSWei Fang 	{  256,  511 },
1625049d8bSWei Fang 	{  512, 1023 },
1725049d8bSWei Fang 	{ 1024, 1522 },
1825049d8bSWei Fang 	{ 1523, NETC_MAX_FRAME_LEN },
1925049d8bSWei Fang 	{ }
2025049d8bSWei Fang };
2125049d8bSWei Fang 
22*beb0e54fSWei Fang static const struct netc_port_stat netc_port_counters[] = {
23*beb0e54fSWei Fang 	{ NETC_PTGSLACR,	"port gate late arrival frames" },
24*beb0e54fSWei Fang 	{ NETC_PSDFTCR,	"port SDF transmit frames" },
25*beb0e54fSWei Fang 	{ NETC_PSDFDDCR,	"port SDF drop duplicate frames" },
26*beb0e54fSWei Fang 	{ NETC_PRXDCR,		"port rx discard frames" },
27*beb0e54fSWei Fang 	{ NETC_PRXDCRRR,	"port rx discard read-reset" },
28*beb0e54fSWei Fang 	{ NETC_PRXDCRR0,	"port rx discard reason 0" },
29*beb0e54fSWei Fang 	{ NETC_PRXDCRR1,	"port rx discard reason 1" },
30*beb0e54fSWei Fang 	{ NETC_PTXDCR,		"port tx discard frames" },
31*beb0e54fSWei Fang 	{ NETC_BPDCR,		"bridge port discard frames" },
32*beb0e54fSWei Fang };
33*beb0e54fSWei Fang 
34*beb0e54fSWei Fang static const struct netc_port_stat netc_emac_counters[] = {
35*beb0e54fSWei Fang 	{ NETC_PM_ROCT(0),	"eMAC rx octets" },
36*beb0e54fSWei Fang 	{ NETC_PM_RVLAN(0),	"eMAC rx VLAN frames" },
37*beb0e54fSWei Fang 	{ NETC_PM_RERR(0),	"eMAC rx frame errors" },
38*beb0e54fSWei Fang 	{ NETC_PM_RUCA(0),	"eMAC rx unicast frames" },
39*beb0e54fSWei Fang 	{ NETC_PM_RDRP(0),	"eMAC rx dropped packets" },
40*beb0e54fSWei Fang 	{ NETC_PM_RPKT(0),	"eMAC rx packets" },
41*beb0e54fSWei Fang 	{ NETC_PM_TOCT(0),	"eMAC tx octets" },
42*beb0e54fSWei Fang 	{ NETC_PM_TVLAN(0),	"eMAC tx VLAN frames" },
43*beb0e54fSWei Fang 	{ NETC_PM_TFCS(0),	"eMAC tx FCS errors" },
44*beb0e54fSWei Fang 	{ NETC_PM_TUCA(0),	"eMAC tx unicast frames" },
45*beb0e54fSWei Fang 	{ NETC_PM_TPKT(0),	"eMAC tx packets" },
46*beb0e54fSWei Fang 	{ NETC_PM_TUND(0),	"eMAC tx undersized packets" },
47*beb0e54fSWei Fang 	{ NETC_PM_TIOCT(0),	"eMAC tx invalid octets" },
48*beb0e54fSWei Fang };
49*beb0e54fSWei Fang 
50*beb0e54fSWei Fang static const struct netc_port_stat netc_pmac_counters[] = {
51*beb0e54fSWei Fang 	{ NETC_PM_ROCT(1),	"pMAC rx octets" },
52*beb0e54fSWei Fang 	{ NETC_PM_RVLAN(1),	"pMAC rx VLAN frames" },
53*beb0e54fSWei Fang 	{ NETC_PM_RERR(1),	"pMAC rx frame errors" },
54*beb0e54fSWei Fang 	{ NETC_PM_RUCA(1),	"pMAC rx unicast frames" },
55*beb0e54fSWei Fang 	{ NETC_PM_RDRP(1),	"pMAC rx dropped packets" },
56*beb0e54fSWei Fang 	{ NETC_PM_RPKT(1),	"pMAC rx packets" },
57*beb0e54fSWei Fang 	{ NETC_PM_TOCT(1),	"pMAC tx octets" },
58*beb0e54fSWei Fang 	{ NETC_PM_TVLAN(1),	"pMAC tx VLAN frames" },
59*beb0e54fSWei Fang 	{ NETC_PM_TFCS(1),	"pMAC tx FCS errors" },
60*beb0e54fSWei Fang 	{ NETC_PM_TUCA(1),	"pMAC tx unicast frames" },
61*beb0e54fSWei Fang 	{ NETC_PM_TPKT(1),	"pMAC tx packets" },
62*beb0e54fSWei Fang 	{ NETC_PM_TUND(1),	"pMAC tx undersized packets" },
63*beb0e54fSWei Fang 	{ NETC_PM_TIOCT(1),	"pMAC tx invalid octets" },
64*beb0e54fSWei Fang };
65*beb0e54fSWei Fang 
6625049d8bSWei Fang static void netc_port_pause_stats(struct netc_port *np, int mac,
6725049d8bSWei Fang 				  struct ethtool_pause_stats *stats)
6825049d8bSWei Fang {
6925049d8bSWei Fang 	if (mac && !np->caps.pmac)
7025049d8bSWei Fang 		return;
7125049d8bSWei Fang 
7225049d8bSWei Fang 	stats->tx_pause_frames = netc_port_rd64(np, NETC_PM_TXPF(mac));
7325049d8bSWei Fang 	stats->rx_pause_frames = netc_port_rd64(np, NETC_PM_RXPF(mac));
7425049d8bSWei Fang }
7525049d8bSWei Fang 
7625049d8bSWei Fang void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
7725049d8bSWei Fang 			       struct ethtool_pause_stats *pause_stats)
7825049d8bSWei Fang {
7925049d8bSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
8025049d8bSWei Fang 	struct net_device *ndev;
8125049d8bSWei Fang 
8225049d8bSWei Fang 	switch (pause_stats->src) {
8325049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_EMAC:
8425049d8bSWei Fang 		netc_port_pause_stats(np, 0, pause_stats);
8525049d8bSWei Fang 		break;
8625049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_PMAC:
8725049d8bSWei Fang 		netc_port_pause_stats(np, 1, pause_stats);
8825049d8bSWei Fang 		break;
8925049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
9025049d8bSWei Fang 		ndev = dsa_to_port(ds, port)->user;
9125049d8bSWei Fang 		ethtool_aggregate_pause_stats(ndev, pause_stats);
9225049d8bSWei Fang 		break;
9325049d8bSWei Fang 	}
9425049d8bSWei Fang }
9525049d8bSWei Fang 
9625049d8bSWei Fang static void netc_port_rmon_stats(struct netc_port *np, int mac,
9725049d8bSWei Fang 				 struct ethtool_rmon_stats *stats)
9825049d8bSWei Fang {
9925049d8bSWei Fang 	if (mac && !np->caps.pmac)
10025049d8bSWei Fang 		return;
10125049d8bSWei Fang 
10225049d8bSWei Fang 	stats->undersize_pkts = netc_port_rd64(np, NETC_PM_RUND(mac));
10325049d8bSWei Fang 	stats->oversize_pkts = netc_port_rd64(np, NETC_PM_ROVR(mac));
10425049d8bSWei Fang 	stats->fragments = netc_port_rd64(np, NETC_PM_RFRG(mac));
10525049d8bSWei Fang 	stats->jabbers = netc_port_rd64(np, NETC_PM_RJBR(mac));
10625049d8bSWei Fang 
10725049d8bSWei Fang 	stats->hist[0] = netc_port_rd64(np, NETC_PM_R64(mac));
10825049d8bSWei Fang 	stats->hist[1] = netc_port_rd64(np, NETC_PM_R127(mac));
10925049d8bSWei Fang 	stats->hist[2] = netc_port_rd64(np, NETC_PM_R255(mac));
11025049d8bSWei Fang 	stats->hist[3] = netc_port_rd64(np, NETC_PM_R511(mac));
11125049d8bSWei Fang 	stats->hist[4] = netc_port_rd64(np, NETC_PM_R1023(mac));
11225049d8bSWei Fang 	stats->hist[5] = netc_port_rd64(np, NETC_PM_R1522(mac));
11325049d8bSWei Fang 	stats->hist[6] = netc_port_rd64(np, NETC_PM_R1523X(mac));
11425049d8bSWei Fang 
11525049d8bSWei Fang 	stats->hist_tx[0] = netc_port_rd64(np, NETC_PM_T64(mac));
11625049d8bSWei Fang 	stats->hist_tx[1] = netc_port_rd64(np, NETC_PM_T127(mac));
11725049d8bSWei Fang 	stats->hist_tx[2] = netc_port_rd64(np, NETC_PM_T255(mac));
11825049d8bSWei Fang 	stats->hist_tx[3] = netc_port_rd64(np, NETC_PM_T511(mac));
11925049d8bSWei Fang 	stats->hist_tx[4] = netc_port_rd64(np, NETC_PM_T1023(mac));
12025049d8bSWei Fang 	stats->hist_tx[5] = netc_port_rd64(np, NETC_PM_T1522(mac));
12125049d8bSWei Fang 	stats->hist_tx[6] = netc_port_rd64(np, NETC_PM_T1523X(mac));
12225049d8bSWei Fang }
12325049d8bSWei Fang 
12425049d8bSWei Fang void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
12525049d8bSWei Fang 			      struct ethtool_rmon_stats *rmon_stats,
12625049d8bSWei Fang 			      const struct ethtool_rmon_hist_range **ranges)
12725049d8bSWei Fang {
12825049d8bSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
12925049d8bSWei Fang 	struct net_device *ndev;
13025049d8bSWei Fang 
13125049d8bSWei Fang 	*ranges = netc_rmon_ranges;
13225049d8bSWei Fang 
13325049d8bSWei Fang 	switch (rmon_stats->src) {
13425049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_EMAC:
13525049d8bSWei Fang 		netc_port_rmon_stats(np, 0, rmon_stats);
13625049d8bSWei Fang 		break;
13725049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_PMAC:
13825049d8bSWei Fang 		netc_port_rmon_stats(np, 1, rmon_stats);
13925049d8bSWei Fang 		break;
14025049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
14125049d8bSWei Fang 		ndev = dsa_to_port(ds, port)->user;
14225049d8bSWei Fang 		ethtool_aggregate_rmon_stats(ndev, rmon_stats);
14325049d8bSWei Fang 		break;
14425049d8bSWei Fang 	}
14525049d8bSWei Fang }
14625049d8bSWei Fang 
14725049d8bSWei Fang static void netc_port_ctrl_stats(struct netc_port *np, int mac,
14825049d8bSWei Fang 				 struct ethtool_eth_ctrl_stats *stats)
14925049d8bSWei Fang {
15025049d8bSWei Fang 	if (mac && !np->caps.pmac)
15125049d8bSWei Fang 		return;
15225049d8bSWei Fang 
15325049d8bSWei Fang 	stats->MACControlFramesTransmitted =
15425049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TCNP(mac));
15525049d8bSWei Fang 	stats->MACControlFramesReceived =
15625049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_RCNP(mac));
15725049d8bSWei Fang }
15825049d8bSWei Fang 
15925049d8bSWei Fang void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
16025049d8bSWei Fang 				  struct ethtool_eth_ctrl_stats *ctrl_stats)
16125049d8bSWei Fang {
16225049d8bSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
16325049d8bSWei Fang 	struct net_device *ndev;
16425049d8bSWei Fang 
16525049d8bSWei Fang 	switch (ctrl_stats->src) {
16625049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_EMAC:
16725049d8bSWei Fang 		netc_port_ctrl_stats(np, 0, ctrl_stats);
16825049d8bSWei Fang 		break;
16925049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_PMAC:
17025049d8bSWei Fang 		netc_port_ctrl_stats(np, 1, ctrl_stats);
17125049d8bSWei Fang 		break;
17225049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
17325049d8bSWei Fang 		ndev = dsa_to_port(ds, port)->user;
17425049d8bSWei Fang 		ethtool_aggregate_ctrl_stats(ndev, ctrl_stats);
17525049d8bSWei Fang 		break;
17625049d8bSWei Fang 	}
17725049d8bSWei Fang }
17825049d8bSWei Fang 
17925049d8bSWei Fang static void netc_port_mac_stats(struct netc_port *np, int mac,
18025049d8bSWei Fang 				struct ethtool_eth_mac_stats *stats)
18125049d8bSWei Fang {
18225049d8bSWei Fang 	if (mac && !np->caps.pmac)
18325049d8bSWei Fang 		return;
18425049d8bSWei Fang 
18525049d8bSWei Fang 	stats->FramesTransmittedOK = netc_port_rd64(np, NETC_PM_TFRM(mac));
18625049d8bSWei Fang 	stats->SingleCollisionFrames = netc_port_rd64(np, NETC_PM_TSCOL(mac));
18725049d8bSWei Fang 	stats->MultipleCollisionFrames =
18825049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TMCOL(mac));
18925049d8bSWei Fang 	stats->FramesReceivedOK = netc_port_rd64(np, NETC_PM_RFRM(mac));
19025049d8bSWei Fang 	stats->FrameCheckSequenceErrors =
19125049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_RFCS(mac));
19225049d8bSWei Fang 	stats->AlignmentErrors = netc_port_rd64(np, NETC_PM_RALN(mac));
19325049d8bSWei Fang 	stats->OctetsTransmittedOK = netc_port_rd64(np, NETC_PM_TEOCT(mac));
19425049d8bSWei Fang 	stats->FramesWithDeferredXmissions =
19525049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TDFR(mac));
19625049d8bSWei Fang 	stats->LateCollisions = netc_port_rd64(np, NETC_PM_TLCOL(mac));
19725049d8bSWei Fang 	stats->FramesAbortedDueToXSColls =
19825049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TECOL(mac));
19925049d8bSWei Fang 	stats->FramesLostDueToIntMACXmitError =
20025049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TERR(mac));
20125049d8bSWei Fang 	stats->OctetsReceivedOK = netc_port_rd64(np, NETC_PM_REOCT(mac));
20225049d8bSWei Fang 	stats->FramesLostDueToIntMACRcvError =
20325049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_RDRNTP(mac));
20425049d8bSWei Fang 	stats->MulticastFramesXmittedOK =
20525049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TMCA(mac));
20625049d8bSWei Fang 	stats->BroadcastFramesXmittedOK =
20725049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TBCA(mac));
20825049d8bSWei Fang 	stats->MulticastFramesReceivedOK =
20925049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_RMCA(mac));
21025049d8bSWei Fang 	stats->BroadcastFramesReceivedOK =
21125049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_RBCA(mac));
21225049d8bSWei Fang 	stats->FramesWithExcessiveDeferral =
21325049d8bSWei Fang 		netc_port_rd64(np, NETC_PM_TEDFR(mac));
21425049d8bSWei Fang }
21525049d8bSWei Fang 
21625049d8bSWei Fang void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
21725049d8bSWei Fang 				 struct ethtool_eth_mac_stats *mac_stats)
21825049d8bSWei Fang {
21925049d8bSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
22025049d8bSWei Fang 	struct net_device *ndev;
22125049d8bSWei Fang 
22225049d8bSWei Fang 	switch (mac_stats->src) {
22325049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_EMAC:
22425049d8bSWei Fang 		netc_port_mac_stats(np, 0, mac_stats);
22525049d8bSWei Fang 		break;
22625049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_PMAC:
22725049d8bSWei Fang 		netc_port_mac_stats(np, 1, mac_stats);
22825049d8bSWei Fang 		break;
22925049d8bSWei Fang 	case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
23025049d8bSWei Fang 		ndev = dsa_to_port(ds, port)->user;
23125049d8bSWei Fang 		ethtool_aggregate_mac_stats(ndev, mac_stats);
23225049d8bSWei Fang 		break;
23325049d8bSWei Fang 	}
23425049d8bSWei Fang }
235*beb0e54fSWei Fang 
236*beb0e54fSWei Fang int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset)
237*beb0e54fSWei Fang {
238*beb0e54fSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
239*beb0e54fSWei Fang 	int size;
240*beb0e54fSWei Fang 
241*beb0e54fSWei Fang 	if (sset != ETH_SS_STATS)
242*beb0e54fSWei Fang 		return -EOPNOTSUPP;
243*beb0e54fSWei Fang 
244*beb0e54fSWei Fang 	size = ARRAY_SIZE(netc_port_counters) +
245*beb0e54fSWei Fang 	       ARRAY_SIZE(netc_emac_counters);
246*beb0e54fSWei Fang 
247*beb0e54fSWei Fang 	if (np->caps.pmac)
248*beb0e54fSWei Fang 		size += ARRAY_SIZE(netc_pmac_counters);
249*beb0e54fSWei Fang 
250*beb0e54fSWei Fang 	return size;
251*beb0e54fSWei Fang }
252*beb0e54fSWei Fang 
253*beb0e54fSWei Fang void netc_port_get_strings(struct dsa_switch *ds, int port,
254*beb0e54fSWei Fang 			   u32 sset, u8 *data)
255*beb0e54fSWei Fang {
256*beb0e54fSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
257*beb0e54fSWei Fang 	int i;
258*beb0e54fSWei Fang 
259*beb0e54fSWei Fang 	if (sset != ETH_SS_STATS)
260*beb0e54fSWei Fang 		return;
261*beb0e54fSWei Fang 
262*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
263*beb0e54fSWei Fang 		ethtool_cpy(&data, netc_port_counters[i].name);
264*beb0e54fSWei Fang 
265*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
266*beb0e54fSWei Fang 		ethtool_cpy(&data, netc_emac_counters[i].name);
267*beb0e54fSWei Fang 
268*beb0e54fSWei Fang 	if (!np->caps.pmac)
269*beb0e54fSWei Fang 		return;
270*beb0e54fSWei Fang 
271*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
272*beb0e54fSWei Fang 		ethtool_cpy(&data, netc_pmac_counters[i].name);
273*beb0e54fSWei Fang }
274*beb0e54fSWei Fang 
275*beb0e54fSWei Fang void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
276*beb0e54fSWei Fang {
277*beb0e54fSWei Fang 	struct netc_port *np = NETC_PORT(ds, port);
278*beb0e54fSWei Fang 	int i;
279*beb0e54fSWei Fang 
280*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
281*beb0e54fSWei Fang 		*data++ = netc_port_rd(np, netc_port_counters[i].reg);
282*beb0e54fSWei Fang 
283*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
284*beb0e54fSWei Fang 		*data++ = netc_port_rd64(np, netc_emac_counters[i].reg);
285*beb0e54fSWei Fang 
286*beb0e54fSWei Fang 	if (!np->caps.pmac)
287*beb0e54fSWei Fang 		return;
288*beb0e54fSWei Fang 
289*beb0e54fSWei Fang 	for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
290*beb0e54fSWei Fang 		*data++ = netc_port_rd64(np, netc_pmac_counters[i].reg);
291*beb0e54fSWei Fang }
292