From dbcc0d01241a1353d8e11e764cf7fcd390ae3f1f Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Thu, 29 Feb 2024 20:16:14 +0800
Subject: [PATCH] net: ethernet: qualcomm: Add PPE port MAC MIB statistics
 functions

Add PPE port MAC MIB statistics functions which are used by netdev
ops and ethtool. For GMAC, a polling task is scheduled to read the
MIB counters periodically to avoid 32bit register counter overflow.

Change-Id: Ic20e240061278f77d703f652e1f7d959db8fac37
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
---
 drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 465 +++++++++++++++++++
 drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  13 +
 drivers/net/ethernet/qualcomm/ppe/ppe_regs.h |  91 ++++
 3 files changed, 569 insertions(+)

--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
@@ -23,6 +23,122 @@
 /* PPE BM port start for PPE MAC ports */
 #define PPE_BM_PORT_MAC_START			7
 
+/* Poll interval time to poll GMAC MIBs for overflow protection,
+ * the time should ensure that the 32bit GMAC packet counter
+ * register would not overflow within this time at line rate
+ * speed for 64B packet size.
+ */
+#define PPE_GMIB_POLL_INTERVAL_MS		120000
+
+#define PPE_MAC_MIB_DESC(_s, _o, _n)		\
+	{					\
+		.size = (_s),			\
+		.offset = (_o),			\
+		.name = (_n),			\
+	}
+
+/* PPE MAC MIB description */
+struct ppe_mac_mib_info {
+	u32 size;
+	u32 offset;
+	const char *name;
+};
+
+/* PPE GMAC MIB statistics type */
+enum ppe_gmib_stats_type {
+	gmib_rx_broadcast,
+	gmib_rx_pause,
+	gmib_rx_multicast,
+	gmib_rx_fcserr,
+	gmib_rx_alignerr,
+	gmib_rx_runt,
+	gmib_rx_frag,
+	gmib_rx_jumbofcserr,
+	gmib_rx_jumboalignerr,
+	gmib_rx_pkt64,
+	gmib_rx_pkt65to127,
+	gmib_rx_pkt128to255,
+	gmib_rx_pkt256to511,
+	gmib_rx_pkt512to1023,
+	gmib_rx_pkt1024to1518,
+	gmib_rx_pkt1519tomax,
+	gmib_rx_toolong,
+	gmib_rx_bytes_g,
+	gmib_rx_bytes_b,
+	gmib_rx_unicast,
+	gmib_tx_broadcast,
+	gmib_tx_pause,
+	gmib_tx_multicast,
+	gmib_tx_underrun,
+	gmib_tx_pkt64,
+	gmib_tx_pkt65to127,
+	gmib_tx_pkt128to255,
+	gmib_tx_pkt256to511,
+	gmib_tx_pkt512to1023,
+	gmib_tx_pkt1024to1518,
+	gmib_tx_pkt1519tomax,
+	gmib_tx_bytes,
+	gmib_tx_collisions,
+	gmib_tx_abortcol,
+	gmib_tx_multicol,
+	gmib_tx_singlecol,
+	gmib_tx_excdeffer,
+	gmib_tx_deffer,
+	gmib_tx_latecol,
+	gmib_tx_unicast,
+};
+
+/* PPE XGMAC MIB statistics type */
+enum ppe_xgmib_stats_type {
+	xgmib_tx_bytes,
+	xgmib_tx_frames,
+	xgmib_tx_broadcast_g,
+	xgmib_tx_multicast_g,
+	xgmib_tx_pkt64,
+	xgmib_tx_pkt65to127,
+	xgmib_tx_pkt128to255,
+	xgmib_tx_pkt256to511,
+	xgmib_tx_pkt512to1023,
+	xgmib_tx_pkt1024tomax,
+	xgmib_tx_unicast,
+	xgmib_tx_multicast,
+	xgmib_tx_broadcast,
+	xgmib_tx_underflow_err,
+	xgmib_tx_bytes_g,
+	xgmib_tx_frames_g,
+	xgmib_tx_pause,
+	xgmib_tx_vlan_g,
+	xgmib_tx_lpi_usec,
+	xgmib_tx_lpi_tran,
+	xgmib_rx_frames,
+	xgmib_rx_bytes,
+	xgmib_rx_bytes_g,
+	xgmib_rx_broadcast_g,
+	xgmib_rx_multicast_g,
+	xgmib_rx_crc_err,
+	xgmib_rx_runt_err,
+	xgmib_rx_jabber_err,
+	xgmib_rx_undersize_g,
+	xgmib_rx_oversize_g,
+	xgmib_rx_pkt64,
+	xgmib_rx_pkt65to127,
+	xgmib_rx_pkt128to255,
+	xgmib_rx_pkt256to511,
+	xgmib_rx_pkt512to1023,
+	xgmib_rx_pkt1024tomax,
+	xgmib_rx_unicast_g,
+	xgmib_rx_len_err,
+	xgmib_rx_outofrange_err,
+	xgmib_rx_pause,
+	xgmib_rx_fifo_overflow,
+	xgmib_rx_vlan,
+	xgmib_rx_wdog_err,
+	xgmib_rx_lpi_usec,
+	xgmib_rx_lpi_tran,
+	xgmib_rx_drop_frames,
+	xgmib_rx_drop_bytes,
+};
+
 /* PPE port clock and reset name */
 static const char * const ppe_port_clk_rst_name[] = {
 	[PPE_PORT_CLK_RST_MAC] = "port_mac",
@@ -30,6 +146,322 @@ static const char * const ppe_port_clk_r
 	[PPE_PORT_CLK_RST_TX] = "port_tx",
 };
 
+/* PPE GMAC MIB statistics description information */
+static const struct ppe_mac_mib_info gmib_info[] = {
+	PPE_MAC_MIB_DESC(4, GMAC_RXBROAD_ADDR, "rx_broadcast"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPAUSE_ADDR, "rx_pause"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXMULTI_ADDR, "rx_multicast"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXFCSERR_ADDR, "rx_fcserr"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXALIGNERR_ADDR, "rx_alignerr"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXRUNT_ADDR, "rx_runt"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXFRAG_ADDR, "rx_frag"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOFCSERR_ADDR, "rx_jumbofcserr"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOALIGNERR_ADDR, "rx_jumboalignerr"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT64_ADDR, "rx_pkt64"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT65TO127_ADDR, "rx_pkt65to127"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT128TO255_ADDR, "rx_pkt128to255"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT256TO511_ADDR, "rx_pkt256to511"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT512TO1023_ADDR, "rx_pkt512to1023"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT1024TO1518_ADDR, "rx_pkt1024to1518"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXPKT1519TOX_ADDR, "rx_pkt1519tomax"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXTOOLONG_ADDR, "rx_toolong"),
+	PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
+	PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_B_ADDR, "rx_bytes_b"),
+	PPE_MAC_MIB_DESC(4, GMAC_RXUNI_ADDR, "rx_unicast"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXBROAD_ADDR, "tx_broadcast"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPAUSE_ADDR, "tx_pause"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXMULTI_ADDR, "tx_multicast"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXUNDERRUN_ADDR, "tx_underrun"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT64_ADDR, "tx_pkt64"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT65TO127_ADDR, "tx_pkt65to127"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT128TO255_ADDR, "tx_pkt128to255"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT256TO511_ADDR, "tx_pkt256to511"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT512TO1023_ADDR, "tx_pkt512to1023"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT1024TO1518_ADDR, "tx_pkt1024to1518"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXPKT1519TOX_ADDR, "tx_pkt1519tomax"),
+	PPE_MAC_MIB_DESC(8, GMAC_TXBYTE_ADDR, "tx_bytes"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXCOLLISIONS_ADDR, "tx_collisions"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXABORTCOL_ADDR, "tx_abortcol"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXMULTICOL_ADDR, "tx_multicol"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXSINGLECOL_ADDR, "tx_singlecol"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXEXCESSIVEDEFER_ADDR, "tx_excdeffer"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXDEFER_ADDR, "tx_deffer"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXLATECOL_ADDR, "tx_latecol"),
+	PPE_MAC_MIB_DESC(4, GMAC_TXUNI_ADDR, "tx_unicast"),
+};
+
+/* PPE XGMAC MIB statistics description information */
+static const struct ppe_mac_mib_info xgmib_info[] = {
+	PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_GB_ADDR, "tx_bytes"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_GB_ADDR, "tx_frames"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_G_ADDR, "tx_broadcast_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_G_ADDR, "tx_multicast_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT64_GB_ADDR, "tx_pkt64"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT65TO127_GB_ADDR, "tx_pkt65to127"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT128TO255_GB_ADDR, "tx_pkt128to255"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT256TO511_GB_ADDR, "tx_pkt256to511"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT512TO1023_GB_ADDR, "tx_pkt512to1023"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT1024TOMAX_GB_ADDR, "tx_pkt1024tomax"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXUNI_GB_ADDR, "tx_unicast"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_GB_ADDR, "tx_multicast"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_GB_ADDR, "tx_broadcast"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXUNDERFLOW_ERR_ADDR, "tx_underflow_err"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_G_ADDR, "tx_bytes_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_G_ADDR, "tx_frames_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXPAUSE_ADDR, "tx_pause"),
+	PPE_MAC_MIB_DESC(8, XGMAC_TXVLAN_G_ADDR, "tx_vlan_g"),
+	PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_USEC_ADDR, "tx_lpi_usec"),
+	PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_TRAN_ADDR, "tx_lpi_tran"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT_GB_ADDR, "rx_frames"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_GB_ADDR, "rx_bytes"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXBROAD_G_ADDR, "rx_broadcast_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXMULTI_G_ADDR, "rx_multicast_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXCRC_ERR_ADDR, "rx_crc_err"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXRUNT_ERR_ADDR, "rx_runt_err"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXJABBER_ERR_ADDR, "rx_jabber_err"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXUNDERSIZE_G_ADDR, "rx_undersize_g"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXOVERSIZE_G_ADDR, "rx_oversize_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT64_GB_ADDR, "rx_pkt64"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT65TO127_GB_ADDR, "rx_pkt65to127"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT128TO255_GB_ADDR, "rx_pkt128to255"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT256TO511_GB_ADDR, "rx_pkt256to511"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT512TO1023_GB_ADDR, "rx_pkt512to1023"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPKT1024TOMAX_GB_ADDR, "rx_pkt1024tomax"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXUNI_G_ADDR, "rx_unicast_g"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXLEN_ERR_ADDR, "rx_len_err"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXOUTOFRANGE_ADDR, "rx_outofrange_err"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXPAUSE_ADDR, "rx_pause"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXFIFOOVERFLOW_ADDR, "rx_fifo_overflow"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXVLAN_GB_ADDR, "rx_vlan"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXWATCHDOG_ERR_ADDR, "rx_wdog_err"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_USEC_ADDR, "rx_lpi_usec"),
+	PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_TRAN_ADDR, "rx_lpi_tran"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARD_GB_ADDR, "rx_drop_frames"),
+	PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARDBYTE_GB_ADDR, "rx_drop_bytes"),
+};
+
+/* Get GMAC MIBs from registers and accumulate to PPE port GMIB stats array */
+static void ppe_port_gmib_update(struct ppe_port *ppe_port)
+{
+	struct ppe_device *ppe_dev = ppe_port->ppe_dev;
+	const struct ppe_mac_mib_info *mib;
+	int port = ppe_port->port_id;
+	u32 reg, val;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(gmib_info); i++) {
+		mib = &gmib_info[i];
+		reg = PPE_PORT_GMAC_ADDR(port) + mib->offset;
+
+		ret = regmap_read(ppe_dev->regmap, reg, &val);
+		if (ret) {
+			dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
+			continue;
+		}
+
+		ppe_port->gmib_stats[i] += val;
+		if (mib->size == 8) {
+			ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
+			if (ret) {
+				dev_warn(ppe_dev->dev, "%s: %d\n",
+					 __func__, ret);
+				continue;
+			}
+
+			ppe_port->gmib_stats[i] += (u64)val << 32;
+		}
+	}
+}
+
+/* Polling task to read GMIB statistics to avoid GMIB 32bit register overflow */
+static void ppe_port_gmib_stats_poll(struct work_struct *work)
+{
+	struct ppe_port *ppe_port = container_of(work, struct ppe_port,
+						 gmib_read.work);
+	spin_lock(&ppe_port->gmib_stats_lock);
+	ppe_port_gmib_update(ppe_port);
+	spin_unlock(&ppe_port->gmib_stats_lock);
+
+	schedule_delayed_work(&ppe_port->gmib_read,
+			      msecs_to_jiffies(PPE_GMIB_POLL_INTERVAL_MS));
+}
+
+/* Get the XGMAC MIB counter based on the specific MIB stats type */
+static u64 ppe_port_xgmib_get(struct ppe_port *ppe_port,
+			      enum ppe_xgmib_stats_type xgmib_type)
+{
+	struct ppe_device *ppe_dev = ppe_port->ppe_dev;
+	const struct ppe_mac_mib_info *mib;
+	int port = ppe_port->port_id;
+	u32 reg, val;
+	u64 data = 0;
+	int ret;
+
+	mib = &xgmib_info[xgmib_type];
+	reg = PPE_PORT_XGMAC_ADDR(port) + mib->offset;
+
+	ret = regmap_read(ppe_dev->regmap, reg, &val);
+	if (ret) {
+		dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
+		goto data_return;
+	}
+
+	data = val;
+	if (mib->size == 8) {
+		ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
+		if (ret) {
+			dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
+			goto data_return;
+		}
+
+		data |= (u64)val << 32;
+	}
+
+data_return:
+	return data;
+}
+
+/**
+ * ppe_port_get_sset_count() - Get PPE port statistics string count
+ * @ppe_port: PPE port
+ * @sset: string set ID
+ *
+ * Description: Get the MAC statistics string count for the PPE port
+ * specified by @ppe_port.
+ *
+ * Return: The count of the statistics string.
+ */
+int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset)
+{
+	if (sset != ETH_SS_STATS)
+		return 0;
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC)
+		return ARRAY_SIZE(gmib_info);
+	else
+		return ARRAY_SIZE(xgmib_info);
+}
+
+/**
+ * ppe_port_get_strings() - Get PPE port statistics strings
+ * @ppe_port: PPE port
+ * @stringset: string set ID
+ * @data: pointer to statistics strings
+ *
+ * Description: Get the MAC statistics stings for the PPE port
+ * specified by @ppe_port. The strings are stored in the buffer
+ * indicated by @data which used in the ethtool ops.
+ */
+void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data)
+{
+	int i;
+
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
+			strscpy(data + i * ETH_GSTRING_LEN, gmib_info[i].name,
+				ETH_GSTRING_LEN);
+	} else {
+		for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
+			strscpy(data + i * ETH_GSTRING_LEN, xgmib_info[i].name,
+				ETH_GSTRING_LEN);
+	}
+}
+
+/**
+ * ppe_port_get_ethtool_stats() - Get PPE port ethtool statistics
+ * @ppe_port: PPE port
+ * @data: pointer to statistics data
+ *
+ * Description: Get the MAC statistics for the PPE port specified
+ * by @ppe_port. The statistics are stored in the buffer indicated
+ * by @data which used in the ethtool ops.
+ */
+void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data)
+{
+	int i;
+
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		spin_lock(&ppe_port->gmib_stats_lock);
+
+		ppe_port_gmib_update(ppe_port);
+		for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
+			data[i] = ppe_port->gmib_stats[i];
+
+		spin_unlock(&ppe_port->gmib_stats_lock);
+	} else {
+		for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
+			data[i] = ppe_port_xgmib_get(ppe_port, i);
+	}
+}
+
+/**
+ * ppe_port_get_stats64() - Get PPE port statistics
+ * @ppe_port: PPE port
+ * @s: statistics pointer
+ *
+ * Description: Get the MAC statistics for the PPE port specified
+ * by @ppe_port.
+ */
+void ppe_port_get_stats64(struct ppe_port *ppe_port,
+			  struct rtnl_link_stats64 *s)
+{
+	if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
+		u64 *src = ppe_port->gmib_stats;
+
+		spin_lock(&ppe_port->gmib_stats_lock);
+
+		ppe_port_gmib_update(ppe_port);
+
+		s->rx_packets = src[gmib_rx_unicast] +
+			src[gmib_rx_broadcast] + src[gmib_rx_multicast];
+
+		s->tx_packets = src[gmib_tx_unicast] +
+			src[gmib_tx_broadcast] + src[gmib_tx_multicast];
+
+		s->rx_bytes = src[gmib_rx_bytes_g];
+		s->tx_bytes = src[gmib_tx_bytes];
+		s->multicast = src[gmib_rx_multicast];
+
+		s->rx_crc_errors = src[gmib_rx_fcserr] + src[gmib_rx_frag];
+		s->rx_frame_errors = src[gmib_rx_alignerr];
+		s->rx_errors = s->rx_crc_errors + s->rx_frame_errors;
+		s->rx_dropped = src[gmib_rx_toolong] + s->rx_errors;
+
+		s->tx_fifo_errors = src[gmib_tx_underrun];
+		s->tx_aborted_errors = src[gmib_tx_abortcol];
+		s->tx_errors = s->tx_fifo_errors + s->tx_aborted_errors;
+		s->collisions = src[gmib_tx_collisions];
+
+		spin_unlock(&ppe_port->gmib_stats_lock);
+	} else {
+		s->multicast = ppe_port_xgmib_get(ppe_port, xgmib_rx_multicast_g);
+
+		s->rx_packets = s->multicast;
+		s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_unicast_g);
+		s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_broadcast_g);
+
+		s->tx_packets = ppe_port_xgmib_get(ppe_port, xgmib_tx_frames);
+		s->rx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_rx_bytes);
+		s->tx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_tx_bytes);
+
+		s->rx_crc_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_crc_err);
+		s->rx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_fifo_overflow);
+
+		s->rx_length_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_len_err);
+		s->rx_errors = s->rx_crc_errors +
+			s->rx_fifo_errors + s->rx_length_errors;
+		s->rx_dropped = s->rx_errors;
+
+		s->tx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_tx_underflow_err);
+		s->tx_errors = s->tx_packets -
+			ppe_port_xgmib_get(ppe_port, xgmib_tx_frames_g);
+	}
+}
+
 /* PPE port and MAC reset */
 static int ppe_port_mac_reset(struct ppe_port *ppe_port)
 {
@@ -261,6 +693,9 @@ static void ppe_port_mac_link_up(struct
 	int ret, port = ppe_port->port_id;
 	u32 reg, val;
 
+	/* Start GMIB statistics polling */
+	schedule_delayed_work(&ppe_port->gmib_read, 0);
+
 	if (mac_type == PPE_MAC_TYPE_GMAC)
 		ret = ppe_port_gmac_link_up(ppe_port,
 					    speed, duplex, tx_pause, rx_pause);
@@ -306,6 +741,9 @@ static void ppe_port_mac_link_down(struc
 	int ret, port = ppe_port->port_id;
 	u32 reg;
 
+	/* Stop GMIB statistics polling */
+	cancel_delayed_work_sync(&ppe_port->gmib_read);
+
 	/* Disable PPE port TX */
 	reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
 	ret = regmap_update_bits(ppe_dev->regmap, reg,
@@ -627,6 +1065,27 @@ static int ppe_port_mac_hw_init(struct p
 	return ret;
 }
 
+/* PPE port MAC MIB work task initialization */
+static int ppe_port_mac_mib_work_init(struct ppe_port *ppe_port)
+{
+	struct ppe_device *ppe_dev = ppe_port->ppe_dev;
+	u64 *gstats;
+
+	gstats = devm_kzalloc(ppe_dev->dev,
+			      sizeof(*gstats) * ARRAY_SIZE(gmib_info),
+			      GFP_KERNEL);
+	if (!gstats)
+		return -ENOMEM;
+
+	ppe_port->gmib_stats = gstats;
+
+	spin_lock_init(&ppe_port->gmib_stats_lock);
+	INIT_DELAYED_WORK(&ppe_port->gmib_read,
+			  ppe_port_gmib_stats_poll);
+
+	return 0;
+}
+
 /**
  * ppe_port_mac_init() - Initialization of PPE ports for the PPE device
  * @ppe_dev: PPE device
@@ -693,6 +1152,12 @@ int ppe_port_mac_init(struct ppe_device
 			goto err_port_node;
 		}
 
+		ret = ppe_port_mac_mib_work_init(&ppe_ports->port[i]);
+		if (ret) {
+			dev_err(ppe_dev->dev, "Failed to initialize MAC MIB work\n");
+			goto err_port_node;
+		}
+
 		i++;
 	}
 
--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
@@ -8,6 +8,8 @@
 
 #include <linux/phylink.h>
 
+struct rtnl_link_stats64;
+
 /**
  * enum ppe_port_clk_rst_type - PPE port clock and reset ID type
  * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC
@@ -44,6 +46,9 @@ enum ppe_mac_type {
  * @port_id: Port ID
  * @clks: Port clocks
  * @rstcs: Port resets
+ * @gmib_read: Delay work task for GMAC MIB statistics polling function
+ * @gmib_stats: GMAC MIB statistics array
+ * @gmib_stats_lock: Lock to protect GMAC MIB statistics
  */
 struct ppe_port {
 	struct phylink *phylink;
@@ -56,6 +61,9 @@ struct ppe_port {
 	int port_id;
 	struct clk *clks[PPE_PORT_CLK_RST_MAX];
 	struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX];
+	struct delayed_work gmib_read;
+	u64 *gmib_stats;
+	spinlock_t gmib_stats_lock; /* Protects GMIB stats */
 };
 
 /**
@@ -73,4 +81,9 @@ void ppe_port_mac_deinit(struct ppe_devi
 int ppe_port_phylink_setup(struct ppe_port *ppe_port,
 			   struct net_device *netdev);
 void ppe_port_phylink_destroy(struct ppe_port *ppe_port);
+int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset);
+void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data);
+void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data);
+void ppe_port_get_stats64(struct ppe_port *ppe_port,
+			  struct rtnl_link_stats64 *s);
 #endif
--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
@@ -618,6 +618,48 @@
 #define GMAC_MIB_CTRL_MASK			\
 	(GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN)
 
+/* GMAC MIB counter registers */
+#define GMAC_RXBROAD_ADDR			0x40
+#define GMAC_RXPAUSE_ADDR			0x44
+#define GMAC_RXMULTI_ADDR			0x48
+#define GMAC_RXFCSERR_ADDR			0x4C
+#define GMAC_RXALIGNERR_ADDR			0x50
+#define GMAC_RXRUNT_ADDR			0x54
+#define GMAC_RXFRAG_ADDR			0x58
+#define GMAC_RXJUMBOFCSERR_ADDR			0x5C
+#define GMAC_RXJUMBOALIGNERR_ADDR		0x60
+#define GMAC_RXPKT64_ADDR			0x64
+#define GMAC_RXPKT65TO127_ADDR			0x68
+#define GMAC_RXPKT128TO255_ADDR			0x6C
+#define GMAC_RXPKT256TO511_ADDR			0x70
+#define GMAC_RXPKT512TO1023_ADDR		0x74
+#define GMAC_RXPKT1024TO1518_ADDR		0x78
+#define GMAC_RXPKT1519TOX_ADDR			0x7C
+#define GMAC_RXTOOLONG_ADDR			0x80
+#define GMAC_RXBYTE_G_ADDR			0x84
+#define GMAC_RXBYTE_B_ADDR			0x8C
+#define GMAC_RXUNI_ADDR				0x94
+#define GMAC_TXBROAD_ADDR			0xA0
+#define GMAC_TXPAUSE_ADDR			0xA4
+#define GMAC_TXMULTI_ADDR			0xA8
+#define GMAC_TXUNDERRUN_ADDR			0xAC
+#define GMAC_TXPKT64_ADDR			0xB0
+#define GMAC_TXPKT65TO127_ADDR			0xB4
+#define GMAC_TXPKT128TO255_ADDR			0xB8
+#define GMAC_TXPKT256TO511_ADDR			0xBC
+#define GMAC_TXPKT512TO1023_ADDR		0xC0
+#define GMAC_TXPKT1024TO1518_ADDR		0xC4
+#define GMAC_TXPKT1519TOX_ADDR			0xC8
+#define GMAC_TXBYTE_ADDR			0xCC
+#define GMAC_TXCOLLISIONS_ADDR			0xD4
+#define GMAC_TXABORTCOL_ADDR			0xD8
+#define GMAC_TXMULTICOL_ADDR			0xDC
+#define GMAC_TXSINGLECOL_ADDR			0xE0
+#define GMAC_TXEXCESSIVEDEFER_ADDR		0xE4
+#define GMAC_TXDEFER_ADDR			0xE8
+#define GMAC_TXLATECOL_ADDR			0xEC
+#define GMAC_TXUNI_ADDR				0xF0
+
 /* XGMAC TX configuration register */
 #define XGMAC_TX_CONFIG_ADDR			0x0
 #define XGMAC_SPEED_M				GENMASK(31, 29)
@@ -680,4 +722,53 @@
 #define XGMAC_MCF				BIT(3)
 #define XGMAC_CNTRST				BIT(0)
 
+/* XGMAC MIB counter registers */
+#define XGMAC_TXBYTE_GB_ADDR			0x814
+#define XGMAC_TXPKT_GB_ADDR			0x81C
+#define XGMAC_TXBROAD_G_ADDR			0x824
+#define XGMAC_TXMULTI_G_ADDR			0x82C
+#define XGMAC_TXPKT64_GB_ADDR			0x834
+#define XGMAC_TXPKT65TO127_GB_ADDR		0x83C
+#define XGMAC_TXPKT128TO255_GB_ADDR		0x844
+#define XGMAC_TXPKT256TO511_GB_ADDR		0x84C
+#define XGMAC_TXPKT512TO1023_GB_ADDR		0x854
+#define XGMAC_TXPKT1024TOMAX_GB_ADDR		0x85C
+#define XGMAC_TXUNI_GB_ADDR			0x864
+#define XGMAC_TXMULTI_GB_ADDR			0x86C
+#define XGMAC_TXBROAD_GB_ADDR			0x874
+#define XGMAC_TXUNDERFLOW_ERR_ADDR		0x87C
+#define XGMAC_TXBYTE_G_ADDR			0x884
+#define XGMAC_TXPKT_G_ADDR			0x88C
+#define XGMAC_TXPAUSE_ADDR			0x894
+#define XGMAC_TXVLAN_G_ADDR			0x89C
+#define XGMAC_TXLPI_USEC_ADDR			0x8A4
+#define XGMAC_TXLPI_TRAN_ADDR			0x8A8
+#define XGMAC_RXPKT_GB_ADDR			0x900
+#define XGMAC_RXBYTE_GB_ADDR			0x908
+#define XGMAC_RXBYTE_G_ADDR			0x910
+#define XGMAC_RXBROAD_G_ADDR			0x918
+#define XGMAC_RXMULTI_G_ADDR			0x920
+#define XGMAC_RXCRC_ERR_ADDR			0x928
+#define XGMAC_RXRUNT_ERR_ADDR			0x930
+#define XGMAC_RXJABBER_ERR_ADDR			0x934
+#define XGMAC_RXUNDERSIZE_G_ADDR		0x938
+#define XGMAC_RXOVERSIZE_G_ADDR			0x93C
+#define XGMAC_RXPKT64_GB_ADDR			0x940
+#define XGMAC_RXPKT65TO127_GB_ADDR		0x948
+#define XGMAC_RXPKT128TO255_GB_ADDR		0x950
+#define XGMAC_RXPKT256TO511_GB_ADDR		0x958
+#define XGMAC_RXPKT512TO1023_GB_ADDR		0x960
+#define XGMAC_RXPKT1024TOMAX_GB_ADDR		0x968
+#define XGMAC_RXUNI_G_ADDR			0x970
+#define XGMAC_RXLEN_ERR_ADDR			0x978
+#define XGMAC_RXOUTOFRANGE_ADDR			0x980
+#define XGMAC_RXPAUSE_ADDR			0x988
+#define XGMAC_RXFIFOOVERFLOW_ADDR		0x990
+#define XGMAC_RXVLAN_GB_ADDR			0x998
+#define XGMAC_RXWATCHDOG_ERR_ADDR		0x9A0
+#define XGMAC_RXLPI_USEC_ADDR			0x9A4
+#define XGMAC_RXLPI_TRAN_ADDR			0x9A8
+#define XGMAC_RXDISCARD_GB_ADDR			0x9AC
+#define XGMAC_RXDISCARDBYTE_GB_ADDR		0x9B4
+
 #endif
