[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