From 6b1f64e2fd938a71ec33db0eaf80b7a7a650af41 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 15:49:53 +0000 Subject: [PATCH] net: phylink: resolve fixed link flow control Resolve the fixed link flow control using the recently introduced linkmode_resolve_pause() helper, which we use in phylink_get_fixed_state() only when operating in full duplex mode. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: hongrongxuan --- drivers/net/phy/phylink.c | 70 +++++++++++++++++---------------------- include/linux/phylink.h | 8 ++--- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index a6f57352abf5..0dcc6530c58b 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -178,9 +178,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, /* We treat the "pause" and "asym-pause" terminology as * defining the link partner's ability. */ if (fwnode_property_read_bool(fixed_node, "pause")) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (fwnode_property_read_bool(fixed_node, "asym-pause")) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); if (ret == 0) { desc = fwnode_get_named_gpiod(fixed_node, "link-gpios", @@ -212,9 +214,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, DUPLEX_FULL : DUPLEX_HALF; pl->link_config.speed = prop[2]; if (prop[3]) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (prop[4]) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); } } @@ -341,6 +345,22 @@ static void phylink_apply_manual_flow(struct phylink *pl, state->pause = pl->link_config.pause; } +static void phylink_resolve_flow(struct phylink_link_state *state) +{ + bool tx_pause, rx_pause; + + state->pause = MLO_PAUSE_NONE; + if (state->duplex == DUPLEX_FULL) { + linkmode_resolve_pause(state->advertising, + state->lp_advertising, + &tx_pause, &rx_pause); + if (tx_pause) + state->pause |= MLO_PAUSE_TX; + if (rx_pause) + state->pause |= MLO_PAUSE_RX; + } +} + static void phylink_mac_config(struct phylink *pl, const struct phylink_link_state *state) { @@ -389,44 +409,16 @@ static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state * /* The fixed state is... fixed except for the link state, * which may be determined by a GPIO or a callback. */ -static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) +static void phylink_get_fixed_state(struct phylink *pl, + struct phylink_link_state *state) { *state = pl->link_config; if (pl->get_fixed_state) pl->get_fixed_state(pl->netdev, state); else if (pl->link_gpio) state->link = !!gpiod_get_value_cansleep(pl->link_gpio); -} -/* Flow control is resolved according to our and the link partners - * advertisements using the following drawn from the 802.3 specs: - * Local device Link partner - * Pause AsymDir Pause AsymDir Result - * 1 X 1 X TX+RX - * 0 1 1 1 TX - * 1 1 0 1 RX - */ -static void phylink_resolve_flow(struct phylink *pl, - struct phylink_link_state *state) -{ - int new_pause = 0; - int pause = 0; - - if (phylink_test(pl->link_config.advertising, Pause)) - pause |= MLO_PAUSE_SYM; - if (phylink_test(pl->link_config.advertising, Asym_Pause)) - pause |= MLO_PAUSE_ASYM; - - pause &= state->pause; - - if (pause & MLO_PAUSE_SYM) - new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; - else if (pause & MLO_PAUSE_ASYM) - new_pause = state->pause & MLO_PAUSE_SYM ? - MLO_PAUSE_TX : MLO_PAUSE_RX; - - state->pause &= ~MLO_PAUSE_TXRX_MASK; - state->pause |= new_pause; + phylink_resolve_flow(state); } static const char *phylink_pause_to_str(int pause) @@ -1352,8 +1344,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, pause->rx_pause != pause->tx_pause) return -EINVAL; - config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); - + config->pause = 0; if (pause->autoneg) config->pause |= MLO_PAUSE_AN; if (pause->rx_pause) @@ -1464,13 +1455,14 @@ static int phylink_mii_emul_read(unsigned int reg, struct phylink_link_state *state) { struct fixed_phy_status fs; + unsigned long *lpa = state->lp_advertising; int val; fs.link = state->link; fs.speed = state->speed; fs.duplex = state->duplex; - fs.pause = state->pause & MLO_PAUSE_SYM; - fs.asym_pause = state->pause & MLO_PAUSE_ASYM; + fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa); + fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa); val = swphy_read_reg(reg, &fs); if (reg == MII_BMSR) { diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 300ecdb6790a..e65b0c5a576b 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -12,12 +12,10 @@ struct net_device; enum { MLO_PAUSE_NONE, - MLO_PAUSE_ASYM = BIT(0), - MLO_PAUSE_SYM = BIT(1), - MLO_PAUSE_RX = BIT(2), - MLO_PAUSE_TX = BIT(3), + MLO_PAUSE_RX = BIT(0), + MLO_PAUSE_TX = BIT(1), MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, - MLO_PAUSE_AN = BIT(4), + MLO_PAUSE_AN = BIT(2), MLO_AN_PHY = 0, /* Conventional PHY */ MLO_AN_FIXED, /* Fixed-link mode */