diff options
-rw-r--r-- | drivers/net/phy/phylink.c | 135 | ||||
-rw-r--r-- | include/linux/phylink.h | 5 |
2 files changed, 128 insertions, 12 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 7f0c49c2b09d..4576395aaeb0 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -156,6 +156,75 @@ static const char *phylink_an_mode_str(unsigned int mode) } /** + * phylink_interface_max_speed() - get the maximum speed of a phy interface + * @interface: phy interface mode defined by &typedef phy_interface_t + * + * Determine the maximum speed of a phy interface. This is intended to help + * determine the correct speed to pass to the MAC when the phy is performing + * rate matching. + * + * Return: The maximum speed of @interface + */ +static int phylink_interface_max_speed(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_REVRMII: + case PHY_INTERFACE_MODE_RMII: + case PHY_INTERFACE_MODE_SMII: + case PHY_INTERFACE_MODE_REVMII: + case PHY_INTERFACE_MODE_MII: + return SPEED_100; + + case PHY_INTERFACE_MODE_TBI: + case PHY_INTERFACE_MODE_MOCA: + case PHY_INTERFACE_MODE_RTBI: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_1000BASEKX: + case PHY_INTERFACE_MODE_TRGMII: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_GMII: + return SPEED_1000; + + case PHY_INTERFACE_MODE_2500BASEX: + return SPEED_2500; + + case PHY_INTERFACE_MODE_5GBASER: + return SPEED_5000; + + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_RXAUI: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_QUSGMII: + return SPEED_10000; + + case PHY_INTERFACE_MODE_25GBASER: + return SPEED_25000; + + case PHY_INTERFACE_MODE_XLGMII: + return SPEED_40000; + + case PHY_INTERFACE_MODE_INTERNAL: + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_MAX: + /* No idea! Garbage in, unknown out */ + return SPEED_UNKNOWN; + } + + /* If we get here, someone forgot to add an interface mode above */ + WARN_ON_ONCE(1); + return SPEED_UNKNOWN; +} + +/** * phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes * @linkmodes: ethtool linkmode mask (must be already initialised) * @caps: bitmask of MAC capabilities @@ -791,11 +860,12 @@ static void phylink_mac_config(struct phylink *pl, const struct phylink_link_state *state) { phylink_dbg(pl, - "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", + "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", __func__, phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), + phy_rate_matching_to_str(state->rate_matching), __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, state->pause, state->link, state->an_enabled); @@ -932,7 +1002,8 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, linkmode_zero(state->lp_advertising); state->interface = pl->link_config.interface; state->an_enabled = pl->link_config.an_enabled; - if (state->an_enabled) { + state->rate_matching = pl->link_config.rate_matching; + if (state->an_enabled) { state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; state->pause = MLO_PAUSE_NONE; @@ -1015,19 +1086,43 @@ static void phylink_link_up(struct phylink *pl, struct phylink_link_state link_state) { struct net_device *ndev = pl->netdev; + int speed, duplex; + bool rx_pause; + + speed = link_state.speed; + duplex = link_state.duplex; + rx_pause = !!(link_state.pause & MLO_PAUSE_RX); + + switch (link_state.rate_matching) { + case RATE_MATCH_PAUSE: + /* The PHY is doing rate matchion from the media rate (in + * the link_state) to the interface speed, and will send + * pause frames to the MAC to limit its transmission speed. + */ + speed = phylink_interface_max_speed(link_state.interface); + duplex = DUPLEX_FULL; + rx_pause = true; + break; + + case RATE_MATCH_CRS: + /* The PHY is doing rate matchion from the media rate (in + * the link_state) to the interface speed, and will cause + * collisions to the MAC to limit its transmission speed. + */ + speed = phylink_interface_max_speed(link_state.interface); + duplex = DUPLEX_HALF; + break; + } pl->cur_interface = link_state.interface; if (pl->pcs && pl->pcs->ops->pcs_link_up) pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode, - pl->cur_interface, - link_state.speed, link_state.duplex); + pl->cur_interface, speed, duplex); - pl->mac_ops->mac_link_up(pl->config, pl->phydev, - pl->cur_link_an_mode, pl->cur_interface, - link_state.speed, link_state.duplex, - !!(link_state.pause & MLO_PAUSE_TX), - !!(link_state.pause & MLO_PAUSE_RX)); + pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, + pl->cur_interface, speed, duplex, + !!(link_state.pause & MLO_PAUSE_TX), rx_pause); if (ndev) netif_carrier_on(ndev); @@ -1119,6 +1214,17 @@ static void phylink_resolve(struct work_struct *w) } link_state.interface = pl->phy_state.interface; + /* If we are doing rate matching, then the + * link speed/duplex comes from the PHY + */ + if (pl->phy_state.rate_matching) { + link_state.rate_matching = + pl->phy_state.rate_matching; + link_state.speed = pl->phy_state.speed; + link_state.duplex = + pl->phy_state.duplex; + } + /* If we have a PHY, we need to update with * the PHY flow control bits. */ @@ -1353,6 +1459,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) mutex_lock(&pl->state_mutex); pl->phy_state.speed = phydev->speed; pl->phy_state.duplex = phydev->duplex; + pl->phy_state.rate_matching = phydev->rate_matching; pl->phy_state.pause = MLO_PAUSE_NONE; if (tx_pause) pl->phy_state.pause |= MLO_PAUSE_TX; @@ -1364,10 +1471,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) phylink_run_resolve(pl); - phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down", + phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s\n", up ? "up" : "down", phy_modes(phydev->interface), phy_speed_to_str(phydev->speed), phy_duplex_to_str(phydev->duplex), + phy_rate_matching_to_str(phydev->rate_matching), phylink_pause_to_str(pl->phy_state.pause)); } @@ -1431,6 +1539,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, pl->phy_state.pause = MLO_PAUSE_NONE; pl->phy_state.speed = SPEED_UNKNOWN; pl->phy_state.duplex = DUPLEX_UNKNOWN; + pl->phy_state.rate_matching = RATE_MATCH_NONE; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); @@ -1873,8 +1982,10 @@ static void phylink_get_ksettings(const struct phylink_link_state *state, { phylink_merge_link_mode(kset->link_modes.advertising, state->advertising); linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising); - kset->base.speed = state->speed; - kset->base.duplex = state->duplex; + if (kset->base.rate_matching == RATE_MATCH_NONE) { + kset->base.speed = state->speed; + kset->base.duplex = state->duplex; + } kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : AUTONEG_DISABLE; } diff --git a/include/linux/phylink.h b/include/linux/phylink.h index cc039ae7e80c..5c99c21e42b5 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -88,6 +88,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode) * @speed: link speed, one of the SPEED_* constants. * @duplex: link duplex mode, one of DUPLEX_* constants. * @pause: link pause state, described by MLO_PAUSE_* constants. + * @rate_matching: rate matching being performed, one of the RATE_MATCH_* + * constants. If rate matching is taking place, then the speed/duplex of + * the medium link mode (@speed and @duplex) and the speed/duplex of the phy + * interface mode (@interface) are different. * @link: true if the link is up. * @an_enabled: true if autonegotiation is enabled/desired. * @an_complete: true if autonegotiation has completed. @@ -99,6 +103,7 @@ struct phylink_link_state { int speed; int duplex; int pause; + int rate_matching; unsigned int link:1; unsigned int an_enabled:1; unsigned int an_complete:1; |