[PATCH net-next 10/17] net: dsa: mv88e6xxx: Add RMU register read/write/wait for bit
From: Luke Howard <hidden>
Date: 2026-07-03 07:47:29
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> Add support for reading a register, writing a register, and waiting for a bit in a register to change, via the remote management unit. If the RMU is not enabled, return -EOPNOTSUPP, and fall back to MDIO. Additionally, if the operation times out, fall back to MDIO. Other errors, such as protocol errors are however reported, rather than falling back to MDIO. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Luke Howard <redacted> --- drivers/net/dsa/mv88e6xxx/chip.c | 18 +++-- drivers/net/dsa/mv88e6xxx/global2.c | 1 + drivers/net/dsa/mv88e6xxx/rmu.c | 139 ++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/rmu.h | 54 +++++++++++++- 4 files changed, 207 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 88cc2326ecc0c..6431d25f3cfa2 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c@@ -65,7 +65,9 @@ int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) assert_reg_lock(chip); - err = mv88e6xxx_smi_read(chip, addr, reg, val); + err = mv88e6xxx_rmu_read(chip, addr, reg, val); + if (mv88e6xxx_rmu_can_mdio_fallback(chip, err)) + err = mv88e6xxx_smi_read(chip, addr, reg, val); if (err) return err;
@@ -81,7 +83,9 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) assert_reg_lock(chip); - err = mv88e6xxx_smi_write(chip, addr, reg, val); + err = mv88e6xxx_rmu_write(chip, addr, reg, val); + if (mv88e6xxx_rmu_can_mdio_fallback(chip, err)) + err = mv88e6xxx_smi_write(chip, addr, reg, val); if (err) return err;
@@ -131,8 +135,14 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, int bit, int val) { - return mv88e6xxx_wait_mask(chip, addr, reg, BIT(bit), - val ? BIT(bit) : 0x0000); + int err; + + err = mv88e6xxx_rmu_wait_bit(chip, addr, reg, bit, val); + + if (mv88e6xxx_rmu_can_mdio_fallback(chip, err)) + err = mv88e6xxx_wait_mask(chip, addr, reg, BIT(bit), + val ? BIT(bit) : 0x0000); + return err; } struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip)
diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c
index 30a6ffa7817b0..834fdb3503f1c 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.c
+++ b/drivers/net/dsa/mv88e6xxx/global2.c@@ -855,6 +855,7 @@ static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip) static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, + MV88E6352_G2_WDOG_CTL_RMU_TIMEOUT | MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE | MV88E6352_G2_WDOG_CTL_QC_ENABLE | MV88E6352_G2_WDOG_CTL_SWRESET);
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
index 65e6bcf4ca938..03c6f47814c85 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.c
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c@@ -105,6 +105,145 @@ static int mv88e6xxx_rmu_request(struct mv88e6xxx_chip *chip, resp, resp_len, timeout_ms); } +int mv88e6xxx_rmu_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) +{ + __be16 req[] = { + MV88E6XXX_RMU_REQ_FORMAT_SOHO, + MV88E6XXX_RMU_REQ_PAD, + MV88E6XXX_RMU_REQ_CODE_REG_RW, + MV88E6XXX_RMU_REQ_RW_0_WRITE(addr, reg), + htons(val), + MV88E6XXX_RMU_REQ_RW_0_END, + MV88E6XXX_RMU_REQ_RW_1_END, + }; + struct mv88e6xxx_rmu_header resp; + int resp_len; + int ret = -1; + + if (chip->rmu_state == MV88E6XXX_RMU_DISABLED) + return -EOPNOTSUPP; + + resp_len = sizeof(resp); + 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 REQ_RW:WRITE %pe addr %d reg %d val %04x\n", + ERR_PTR(ret), addr, reg, val); + return ret; + } + + if (ret < resp_len) { + dev_err(chip->dev, "RMU: write returned wrong length: rx %d expecting %d\n", + ret, resp_len); + return -EPROTO; + } + + if (resp.code != MV88E6XXX_RMU_RESP_CODE_REG_RW) { + dev_err(chip->dev, "RMU: write returned wrong code %d\n", + be16_to_cpu(resp.code)); + return -EPROTO; + } + + return 0; +} + +int mv88e6xxx_rmu_read(struct mv88e6xxx_chip *chip, int addr, int reg, + u16 *val) +{ + __be16 req[] = { + MV88E6XXX_RMU_REQ_FORMAT_SOHO, + MV88E6XXX_RMU_REQ_PAD, + MV88E6XXX_RMU_REQ_CODE_REG_RW, + MV88E6XXX_RMU_REQ_RW_0_READ(addr, reg), + 0, + MV88E6XXX_RMU_REQ_RW_0_END, + MV88E6XXX_RMU_REQ_RW_1_END, + }; + struct mv88e6xxx_rmu_rw_resp resp; + int resp_len; + int ret = -1; + + if (chip->rmu_state == MV88E6XXX_RMU_DISABLED) + return -EOPNOTSUPP; + + resp_len = sizeof(resp); + 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 REQ_RW:READ %pe addr %d reg %d\n", + ERR_PTR(ret), addr, reg); + return ret; + } + + if (ret < resp_len) { + dev_err(chip->dev, "RMU: read returned wrong length: rx %d expecting %d\n", + ret, resp_len); + return -EPROTO; + } + + if (resp.rmu_header.code != MV88E6XXX_RMU_RESP_CODE_REG_RW) { + dev_err(chip->dev, "RMU: read returned wrong code %d\n", + be16_to_cpu(resp.rmu_header.code)); + return -EPROTO; + } + + *val = ntohs(resp.value); + + return 0; +} + +int mv88e6xxx_rmu_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, + int bit, int val) +{ + __be16 req[] = { + MV88E6XXX_RMU_REQ_FORMAT_SOHO, + MV88E6XXX_RMU_REQ_PAD, + MV88E6XXX_RMU_REQ_CODE_REG_RW, + val ? MV88E6XXX_RMU_REQ_RW_0_WAIT_1(addr, reg) : + MV88E6XXX_RMU_REQ_RW_0_WAIT_0(addr, reg), + htons((bit & 0xf) << 8), + MV88E6XXX_RMU_REQ_RW_0_END, + MV88E6XXX_RMU_REQ_RW_1_END, + }; + struct mv88e6xxx_rmu_rw_resp resp; + int resp_len; + int ret = -1; + + if (chip->rmu_state == MV88E6XXX_RMU_DISABLED) + return -EOPNOTSUPP; + + resp_len = sizeof(resp); + ret = mv88e6xxx_rmu_request(chip, req, sizeof(req), + &resp, resp_len, + MV88E6XXX_RMU_WAIT_BIT_TIMEOUT_MS); + if (ret < 0) { + dev_dbg(chip->dev, "RMU: error for command REQ_RW:WAIT %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (ret < resp_len) { + dev_err(chip->dev, "RMU: wait on bit returned wrong length: rx %d expecting %d\n", + ret, resp_len); + return -EPROTO; + } + + if (resp.rmu_header.code != MV88E6XXX_RMU_RESP_CODE_REG_RW) { + dev_err(chip->dev, "RMU: wait on bit returned wrong code %d\n", + be16_to_cpu(resp.rmu_header.code)); + return -EPROTO; + } + + if ((ntohs(resp.value) & 0xff) == 0xff) { + dev_err(chip->dev, "RMU: wait on bit timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int mv88e6xxx_rmu_get_id(struct mv88e6xxx_chip *chip) { const __be16 req[4] = {
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
index a81af5a1ad762..4b9df3119d517 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.h
+++ b/drivers/net/dsa/mv88e6xxx/rmu.h@@ -11,17 +11,45 @@ #define MV88E6XXX_RMU_WAIT_TIME_MS 20 #define MV88E6XXX_RMU_REQUEST_TIMEOUT_MS 50 +#define MV88E6XXX_RMU_WAIT_BIT_TIMEOUT_MS 1000 #define MV88E6XXX_RMU_REQ_FORMAT_GET_ID htons(0x0000) #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_REG_RW htons(0x2000) #define MV88E6XXX_RMU_REQ_DATA htons(0x0000) +#define MV88E6XXX_RMU_REQ_RW_0_OP_WAIT_1 (0x7 << 10) +#define MV88E6XXX_RMU_REQ_RW_0_OP_WAIT_0 (0x4 << 10) +#define MV88E6XXX_RMU_REQ_RW_0_OP_READ (0x2 << 10) +#define MV88E6XXX_RMU_REQ_RW_0_OP_WRITE (0x1 << 10) + +#define MV88E6XXX_RMU_REQ_RW_0_READ(_addr_, _reg_) \ + htons(MV88E6XXX_RMU_REQ_RW_0_OP_READ | \ + ((_addr_) << 5) | \ + (_reg_)) +#define MV88E6XXX_RMU_REQ_RW_0_WRITE(_addr_, _reg_) \ + htons(MV88E6XXX_RMU_REQ_RW_0_OP_WRITE | \ + ((_addr_) << 5) | \ + (_reg_)) + +#define MV88E6XXX_RMU_REQ_RW_0_WAIT_0(_addr_, _reg_) \ + htons(MV88E6XXX_RMU_REQ_RW_0_OP_WAIT_0 | \ + ((_addr_) << 5) | \ + (_reg_)) +#define MV88E6XXX_RMU_REQ_RW_0_WAIT_1(_addr_, _reg_) \ + htons(MV88E6XXX_RMU_REQ_RW_0_OP_WAIT_1 | \ + ((_addr_) << 5) | \ + (_reg_)) + +#define MV88E6XXX_RMU_REQ_RW_0_END htons(0xffff) +#define MV88E6XXX_RMU_REQ_RW_1_END htons(0xffff) + #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_REG_RW htons(0x2000) struct mv88e6xxx_rmu_header { __be16 format;
@@ -29,10 +57,34 @@ struct mv88e6xxx_rmu_header { __be16 code; } __packed; +struct mv88e6xxx_rmu_rw_resp { + struct mv88e6xxx_rmu_header rmu_header; + __be16 cmd; + __be16 value; + __be16 end0; + __be16 end1; +} __packed; + +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, + u16 *val); +int mv88e6xxx_rmu_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, + int bit, int val); void mv88e6xxx_rmu_conduit_state_change(struct dsa_switch *ds, const struct net_device *conduit, bool operational); void mv88e6xxx_rmu_frame2reg_handler(struct dsa_switch *ds, struct sk_buff *skb, u8 seqno); + +/* RMU register access falls back to MDIO when the operation is unsupported or + * times out. @chip is unused today, but threading it through here lets a future + * RMU-only mode suppress the fallback without touching any of the call sites. + */ +static inline bool +mv88e6xxx_rmu_can_mdio_fallback(struct mv88e6xxx_chip *chip, int err) +{ + return err == -EOPNOTSUPP || err == -ETIMEDOUT; +} #endif /* _MV88E6XXX_RMU_H_ */
--
2.43.0