Program the pause status resolved by phylink into the Port MAC Control register when forcing the link up, and return flow control to hardware resolution when forcing the link down, mirroring the existing treatment of speed and duplex. Flow control is programmed before port_sync_link() so that the port's MAC control is not modified while the link is forced up. The MAC has a single flow control enable covering both directions; as the driver only advertises MAC_SYM_PAUSE, rx_pause and tx_pause are always equal here. A fixed-link port whose device tree node lacks the "pause" property now has flow control explicitly disabled while the link is forced up, rather than being left at the chip's default. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 9 +++++++++ drivers/net/dsa/mv88e6xxx/port.c | 20 ++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 3 files changed, 31 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 80b877c74513d..73c28bf2bf3cb 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -974,6 +974,10 @@ static void mv88e6xxx_mac_link_down(struct phylink_config *config, mode == MLO_AN_FIXED) && ops->port_sync_link) err = ops->port_sync_link(chip, port, mode, false); + if (!err) + err = mv88e6xxx_port_set_force_flow_ctl(chip, port, false, + false); + if (!err && ops->port_set_speed_duplex) err = ops->port_set_speed_duplex(chip, port, SPEED_UNFORCED, DUPLEX_UNFORCED); @@ -1012,6 +1016,11 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config, goto error; } + err = mv88e6xxx_port_set_force_flow_ctl(chip, port, true, + rx_pause); + if (err) + goto error; + if (ops->port_sync_link) err = ops->port_sync_link(chip, port, mode, true); } diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index ea1fab71968a0..7ee802cf3412c 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -182,6 +182,26 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) return 0; } +int mv88e6xxx_port_set_force_flow_ctl(struct mv88e6xxx_chip *chip, int port, + bool force, bool value) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_FC | MV88E6XXX_PORT_MAC_CTL_FC); + if (force) { + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_FC; + if (value) + reg |= MV88E6XXX_PORT_MAC_CTL_FC; + } + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); +} + int mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup) { const struct mv88e6xxx_ops *ops = chip->info->ops; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 5b6cde9f7406f..f98df6351f5b7 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -485,6 +485,8 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); +int mv88e6xxx_port_set_force_flow_ctl(struct mv88e6xxx_chip *chip, int port, + bool force, bool value); int mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup); int mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup); --- base-commit: 2bb62a85aff6d4c14a62a476dfabaada3c1cc014 change-id: 20260701-mv88e6xxx-flow-control-cafe32352712 Best regards, -- Luke Howard