From: Russell King Date: Tue, 3 Jan 2017 18:34:17 +0000 Subject: [PATCH] phylink: propagate PHY interface mode to MAC driver Some 10Gigabit PHYs automatically switch the mode of their host interface depending on their negotiated speed. We need to communicate this to the MAC driver so the MAC can switch its host interface to match the PHYs new operating mode. Provide the current PHY interface mode to the MAC driver. Signed-off-by: Russell King --- --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -242,8 +242,9 @@ static void phylink_mac_config(struct ph const struct phylink_link_state *state) { netdev_dbg(pl->netdev, - "%s: mode=%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", + "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", __func__, phylink_an_mode_str(pl->link_an_mode), + phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, @@ -264,6 +265,7 @@ static int phylink_get_mac_state(struct linkmode_copy(state->advertising, pl->link_config.advertising); linkmode_zero(state->lp_advertising); + state->interface = pl->link_config.interface; state->an_enabled = pl->link_config.an_enabled; state->link = 1; @@ -344,19 +346,38 @@ static void phylink_resolve(struct work_ case MLO_AN_PHY: link_state = pl->phy_state; phylink_resolve_flow(pl, &link_state); + phylink_mac_config(pl, &link_state); break; case MLO_AN_FIXED: phylink_get_fixed_state(pl, &link_state); + phylink_mac_config(pl, &link_state); break; case MLO_AN_SGMII: phylink_get_mac_state(pl, &link_state); if (pl->phydev) { + bool changed = false; + link_state.link = link_state.link && pl->phy_state.link; - link_state.pause |= pl->phy_state.pause; - phylink_resolve_flow(pl, &link_state); + + if (pl->phy_state.interface != + link_state.interface) { + link_state.interface = pl->phy_state.interface; + changed = true; + } + + /* Propagate the flow control from the PHY + * to the MAC. Also propagate the interface + * if changed. + */ + if (pl->phy_state.link || changed) { + link_state.pause |= pl->phy_state.pause; + phylink_resolve_flow(pl, &link_state); + + phylink_mac_config(pl, &link_state); + } } break; @@ -372,13 +393,6 @@ static void phylink_resolve(struct work_ pl->ops->mac_link_down(ndev, pl->link_an_mode); netdev_info(ndev, "Link is Down\n"); } else { - /* If we have a PHY, we need the MAC updated with - * the current link parameters (eg, in SGMII mode, - * with flow control status.) - */ - if (pl->phydev) - phylink_mac_config(pl, &link_state); - pl->ops->mac_link_up(ndev, pl->link_an_mode, pl->phydev); @@ -414,8 +428,10 @@ struct phylink *phylink_create(struct ne mutex_init(&pl->config_mutex); INIT_WORK(&pl->resolve, phylink_resolve); pl->netdev = ndev; + pl->phy_state.interface = iface; pl->link_interface = iface; pl->link_port = PORT_MII; + pl->link_config.interface = iface; pl->link_config.pause = MLO_PAUSE_AN; pl->link_config.speed = SPEED_UNKNOWN; pl->link_config.duplex = DUPLEX_UNKNOWN; @@ -471,12 +487,14 @@ void phylink_phy_change(struct phy_devic pl->phy_state.pause |= MLO_PAUSE_SYM; if (phydev->asym_pause) pl->phy_state.pause |= MLO_PAUSE_ASYM; + pl->phy_state.interface = phydev->interface; pl->phy_state.link = up; mutex_unlock(&pl->state_mutex); phylink_run_resolve(pl); - netdev_dbg(pl->netdev, "phy link %s %s/%s\n", up ? "up" : "down", + netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down", + phy_modes(phydev->interface), phy_speed_to_str(phydev->speed), phy_duplex_to_str(phydev->duplex)); } --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -27,6 +27,7 @@ enum { struct phylink_link_state { __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); + phy_interface_t interface; /* PHY_INTERFACE_xxx */ int speed; int duplex; int pause;