Thread (27 messages) 27 messages, 3 authors, 1d ago
WARM1d

[PATCH net-next 13/17] net: dsa: mv88e6xxx: Get some MIB stats via the RMU

From: Luke Howard <hidden>
Date: 2026-07-03 07:47:42
Also in: lkml
Subsystem: marvell 88e6xxx ethernet switch fabric driver, networking drivers, networking [dsa], the rest · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Vladimir Oltean, Linus Torvalds

From: Andrew Lunn <andrew@lunn.ch>

It is possible to access the bank 0 and the port statistics using an
RMU request. Bank 1 stats are not available in this way. Issue an RMU
request to get the statistics which are available, and use the big
table of statistics to decode the reply and return statistics in a way
that is compatible with the MDIO method.

Where bank 1 stats cannot be retrieved via RMU, fall back to individual
register reads and writes.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Luke Howard <redacted>
---
 drivers/net/dsa/mv88e6xxx/chip.c |  15 ++++--
 drivers/net/dsa/mv88e6xxx/chip.h |   4 ++
 drivers/net/dsa/mv88e6xxx/rmu.c  | 101 +++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/rmu.h  |  20 ++++++++
 4 files changed, 137 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 6431d25f3cfa2..531a59093ef97 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1337,9 +1337,9 @@ static size_t mv88e6390_stats_get_stat(struct mv88e6xxx_chip *chip, int port,
 	return 1;
 }
 
-static size_t mv88e6xxx_stats_get_stat(struct mv88e6xxx_chip *chip, int port,
-				       const struct mv88e6xxx_hw_stat *stat,
-				       uint64_t *data)
+size_t mv88e6xxx_stats_get_stat(struct mv88e6xxx_chip *chip, int port,
+				const struct mv88e6xxx_hw_stat *stat,
+				uint64_t *data)
 {
 	int ret = 0;
 
@@ -1360,6 +1360,15 @@ static size_t mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
 {
 	const struct mv88e6xxx_hw_stat *stat;
 	size_t i, j;
+	int err;
+
+	err = mv88e6xxx_rmu_stats(chip, port, data, mv88e6xxx_hw_stats,
+				  ARRAY_SIZE(mv88e6xxx_hw_stats));
+	if (err > 0)
+		return err;
+
+	if (!mv88e6xxx_rmu_can_mdio_fallback(chip, err))
+		return err;
 
 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
 		stat = &mv88e6xxx_hw_stats[i];
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 07300c25cbd36..7e483ba8c58d9 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -905,4 +905,8 @@ int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
 				 void *priv),
 		       void *priv);
 
+size_t mv88e6xxx_stats_get_stat(struct mv88e6xxx_chip *chip, int port,
+				const struct mv88e6xxx_hw_stat *stat,
+				uint64_t *data);
+
 #endif /* _MV88E6XXX_CHIP_H */
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
index 03c6f47814c85..88b02030a774f 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.c
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c
@@ -10,6 +10,7 @@
 #include <net/dsa.h>
 #include "chip.h"
 #include "global1.h"
+#include "port.h"
 #include "rmu.h"
 
 static const u8 mv88e6xxx_rmu_dest_addr[ETH_ALEN] = {
@@ -105,6 +106,106 @@ static int mv88e6xxx_rmu_request(struct mv88e6xxx_chip *chip,
 				  resp, resp_len, timeout_ms);
 }
 
+int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port,
+			u64 *data,
+			const struct mv88e6xxx_hw_stat *hw_stats,
+			int num_hw_stats)
+{
+	__be16 req[] = {
+		MV88E6XXX_RMU_REQ_FORMAT_SOHO,
+		MV88E6XXX_RMU_REQ_PAD,
+		MV88E6XXX_RMU_REQ_CODE_MIB,
+		htons(port),
+	};
+	const struct mv88e6xxx_hw_stat *stat;
+	struct mv88e6xxx_rmu_mib_resp resp;
+	int i, j, ret;
+	int resp_len;
+	u64 high;
+
+	if (chip->rmu_state == MV88E6XXX_RMU_DISABLED)
+		return -EOPNOTSUPP;
+
+	resp_len = sizeof(resp) - sizeof(resp.data);
+	if (chip->info->stats_type & STATS_TYPE_BANK0)
+		resp_len += MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN;
+	if (chip->info->stats_type & STATS_TYPE_PORT)
+		resp_len += MV88E6XXX_RMU_STATS_TYPE_PORT_LEN;
+	else if (chip->info->stats_type & STATS_TYPE_BANK1)
+		resp_len += MV88E6XXX_RMU_STATS_TYPE_DATA1_LEN;
+
+	ret = mv88e6xxx_rmu_request(chip, req, sizeof(req),
+				    &resp, resp_len,
+				    MV88E6XXX_RMU_REQUEST_TIMEOUT_MS);
+	if (ret < 0) {
+		dev_dbg(chip->dev, "RMU: error for command MIB %pe\n",
+			ERR_PTR(ret));
+		return ret;
+	}
+
+	if (ret < resp_len) {
+		dev_err(chip->dev, "RMU: MIB returned wrong length: rx %d expecting %d\n",
+			ret, resp_len);
+		return -EPROTO;
+	}
+
+	if (resp.rmu_header.code != MV88E6XXX_RMU_RESP_CODE_MIB) {
+		dev_err(chip->dev, "RMU: MIB returned wrong code %d\n",
+			be16_to_cpu(resp.rmu_header.code));
+		return -EPROTO;
+	}
+
+	for (i = 0, j = 0; i < num_hw_stats; i++) {
+		stat = &hw_stats[i];
+
+		if ((stat->type & chip->info->stats_type) == 0) {
+			/* Not available via RMU, use SMI (if available) */
+			j += mv88e6xxx_stats_get_stat(chip, port, stat, &data[j]);
+			continue;
+		}
+
+		if (stat->type & STATS_TYPE_PORT) {
+			__be16 *port = (__be16 *)resp.data;
+
+			if (chip->info->stats_type & STATS_TYPE_BANK0)
+				port += MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN / 2;
+
+			switch (stat->reg) {
+			case MV88E6XXX_PORT_IN_DISCARD_LO:
+				data[j] = be16_to_cpu(port[0]) << 16;
+				data[j] |= be16_to_cpu(port[1]);
+				break;
+			case MV88E6XXX_PORT_IN_FILTERED:
+				data[j] = be16_to_cpu(port[3]);
+				break;
+			case MV88E6XXX_PORT_OUT_FILTERED:
+				data[j] = be16_to_cpu(port[5]);
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+
+		if (stat->type & (STATS_TYPE_BANK0 | STATS_TYPE_BANK1)) {
+			int reg = stat->reg;
+
+			if (stat->type & STATS_TYPE_BANK1 &&
+			    (chip->info->stats_type & STATS_TYPE_BANK0))
+				reg += MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN / 4;
+
+			data[j] = be32_to_cpu(resp.data[reg]);
+			if (stat->size == 8) {
+				high = be32_to_cpu(resp.data[reg + 1]);
+				data[j] |= (high << 32);
+			}
+		}
+
+		j++;
+	}
+
+	return j;
+}
+
 int mv88e6xxx_rmu_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
 {
 	__be16 req[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
index 4b9df3119d517..0865e899cb340 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.h
+++ b/drivers/net/dsa/mv88e6xxx/rmu.h
@@ -17,6 +17,7 @@
 #define MV88E6XXX_RMU_REQ_FORMAT_SOHO		htons(0x0001)
 #define MV88E6XXX_RMU_REQ_PAD			htons(0x0000)
 #define MV88E6XXX_RMU_REQ_CODE_GET_ID		htons(0x0000)
+#define MV88E6XXX_RMU_REQ_CODE_MIB		htons(0x1020)
 #define MV88E6XXX_RMU_REQ_CODE_REG_RW		htons(0x2000)
 #define MV88E6XXX_RMU_REQ_DATA			htons(0x0000)
 
@@ -49,6 +50,7 @@
 #define MV88E6XXX_RMU_RESP_FORMAT_1		htons(0x0001)
 #define MV88E6XXX_RMU_RESP_FORMAT_2		htons(0x0002)
 #define MV88E6XXX_RMU_RESP_CODE_GOT_ID		htons(0x0000)
+#define MV88E6XXX_RMU_RESP_CODE_MIB		htons(0x1020)
 #define MV88E6XXX_RMU_RESP_CODE_REG_RW		htons(0x2000)
 
 struct mv88e6xxx_rmu_header {
@@ -65,6 +67,24 @@ struct mv88e6xxx_rmu_rw_resp {
 	__be16 end1;
 } __packed;
 
+/* number of RMU MIB statistics, by type, in octets */
+#define MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN	128
+#define MV88E6XXX_RMU_STATS_TYPE_PORT_LEN	12
+#define MV88E6XXX_RMU_STATS_TYPE_DATA1_LEN	128
+#define MV88E6XXX_RMU_STATS_TYPE_MAX_LEN	(MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN + \
+						 MV88E6XXX_RMU_STATS_TYPE_DATA1_LEN)
+
+struct mv88e6xxx_rmu_mib_resp {
+	struct mv88e6xxx_rmu_header rmu_header;
+	__be16 swport;
+	__be32 timestamp;
+	__be32 data[MV88E6XXX_RMU_STATS_TYPE_MAX_LEN / 4];
+} __packed;
+
+int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port,
+			u64 *data,
+			const struct mv88e6xxx_hw_stat *hw_stats,
+			int num_hw_stats);
 int mv88e6xxx_rmu_write(struct mv88e6xxx_chip *chip, int addr, int reg,
 			u16 val);
 int mv88e6xxx_rmu_read(struct mv88e6xxx_chip *chip, int addr, int reg,
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help