Commit ae0e4bb2 authored by Sean Anderson's avatar Sean Anderson Committed by David S. Miller

net: phylink: Adjust link settings based on rate matching

If the phy is configured to use pause-based rate matching, ensure that
the link is full duplex with pause frame reception enabled. As
suggested, if pause-based rate matching is enabled by the phy, then
pause reception is unconditionally enabled.

The interface duplex is determined based on the rate matching type.
When rate matching is enabled, so is the speed. We assume the maximum
interface speed is used. This is only relevant for MLO_AN_PHY. For
MLO_AN_INBAND, the MAC/PCS's view of the interface speed will be used.

Although there are no RATE_ADAPT_CRS phys in-tree, it has been added for
comparison (and the implementation is quite simple).
Co-developed-by: default avatarRussell King <linux@armlinux.org.uk>
Signed-off-by: default avatarSean Anderson <sean.anderson@seco.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0c3e10cb
...@@ -155,6 +155,75 @@ static const char *phylink_an_mode_str(unsigned int mode) ...@@ -155,6 +155,75 @@ static const char *phylink_an_mode_str(unsigned int mode)
return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
} }
/**
* 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 * phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes
* @linkmodes: ethtool linkmode mask (must be already initialised) * @linkmodes: ethtool linkmode mask (must be already initialised)
...@@ -791,11 +860,12 @@ static void phylink_mac_config(struct phylink *pl, ...@@ -791,11 +860,12 @@ static void phylink_mac_config(struct phylink *pl,
const struct phylink_link_state *state) const struct phylink_link_state *state)
{ {
phylink_dbg(pl, 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), __func__, phylink_an_mode_str(pl->cur_link_an_mode),
phy_modes(state->interface), phy_modes(state->interface),
phy_speed_to_str(state->speed), phy_speed_to_str(state->speed),
phy_duplex_to_str(state->duplex), phy_duplex_to_str(state->duplex),
phy_rate_matching_to_str(state->rate_matching),
__ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
state->pause, state->link, state->an_enabled); state->pause, state->link, state->an_enabled);
...@@ -932,7 +1002,8 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, ...@@ -932,7 +1002,8 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
linkmode_zero(state->lp_advertising); linkmode_zero(state->lp_advertising);
state->interface = pl->link_config.interface; state->interface = pl->link_config.interface;
state->an_enabled = pl->link_config.an_enabled; 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->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN; state->duplex = DUPLEX_UNKNOWN;
state->pause = MLO_PAUSE_NONE; state->pause = MLO_PAUSE_NONE;
...@@ -1015,19 +1086,43 @@ static void phylink_link_up(struct phylink *pl, ...@@ -1015,19 +1086,43 @@ static void phylink_link_up(struct phylink *pl,
struct phylink_link_state link_state) struct phylink_link_state link_state)
{ {
struct net_device *ndev = pl->netdev; 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; pl->cur_interface = link_state.interface;
if (pl->pcs && pl->pcs->ops->pcs_link_up) if (pl->pcs && pl->pcs->ops->pcs_link_up)
pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
pl->cur_interface, pl->cur_interface, speed, duplex);
link_state.speed, link_state.duplex);
pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
pl->cur_link_an_mode, pl->cur_interface, pl->cur_interface, speed, duplex,
link_state.speed, link_state.duplex, !!(link_state.pause & MLO_PAUSE_TX), rx_pause);
!!(link_state.pause & MLO_PAUSE_TX),
!!(link_state.pause & MLO_PAUSE_RX));
if (ndev) if (ndev)
netif_carrier_on(ndev); netif_carrier_on(ndev);
...@@ -1119,6 +1214,17 @@ static void phylink_resolve(struct work_struct *w) ...@@ -1119,6 +1214,17 @@ static void phylink_resolve(struct work_struct *w)
} }
link_state.interface = pl->phy_state.interface; 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 /* If we have a PHY, we need to update with
* the PHY flow control bits. * the PHY flow control bits.
*/ */
...@@ -1353,6 +1459,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) ...@@ -1353,6 +1459,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
mutex_lock(&pl->state_mutex); mutex_lock(&pl->state_mutex);
pl->phy_state.speed = phydev->speed; pl->phy_state.speed = phydev->speed;
pl->phy_state.duplex = phydev->duplex; pl->phy_state.duplex = phydev->duplex;
pl->phy_state.rate_matching = phydev->rate_matching;
pl->phy_state.pause = MLO_PAUSE_NONE; pl->phy_state.pause = MLO_PAUSE_NONE;
if (tx_pause) if (tx_pause)
pl->phy_state.pause |= MLO_PAUSE_TX; pl->phy_state.pause |= MLO_PAUSE_TX;
...@@ -1364,10 +1471,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) ...@@ -1364,10 +1471,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
phylink_run_resolve(pl); 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_modes(phydev->interface),
phy_speed_to_str(phydev->speed), phy_speed_to_str(phydev->speed),
phy_duplex_to_str(phydev->duplex), phy_duplex_to_str(phydev->duplex),
phy_rate_matching_to_str(phydev->rate_matching),
phylink_pause_to_str(pl->phy_state.pause)); phylink_pause_to_str(pl->phy_state.pause));
} }
...@@ -1431,6 +1539,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, ...@@ -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.pause = MLO_PAUSE_NONE;
pl->phy_state.speed = SPEED_UNKNOWN; pl->phy_state.speed = SPEED_UNKNOWN;
pl->phy_state.duplex = DUPLEX_UNKNOWN; pl->phy_state.duplex = DUPLEX_UNKNOWN;
pl->phy_state.rate_matching = RATE_MATCH_NONE;
linkmode_copy(pl->supported, supported); linkmode_copy(pl->supported, supported);
linkmode_copy(pl->link_config.advertising, config.advertising); linkmode_copy(pl->link_config.advertising, config.advertising);
...@@ -1873,8 +1982,10 @@ static void phylink_get_ksettings(const struct phylink_link_state *state, ...@@ -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); phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising); linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
kset->base.speed = state->speed; if (kset->base.rate_matching == RATE_MATCH_NONE) {
kset->base.duplex = state->duplex; kset->base.speed = state->speed;
kset->base.duplex = state->duplex;
}
kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
AUTONEG_DISABLE; AUTONEG_DISABLE;
} }
......
...@@ -88,6 +88,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode) ...@@ -88,6 +88,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
* @speed: link speed, one of the SPEED_* constants. * @speed: link speed, one of the SPEED_* constants.
* @duplex: link duplex mode, one of DUPLEX_* constants. * @duplex: link duplex mode, one of DUPLEX_* constants.
* @pause: link pause state, described by MLO_PAUSE_* 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. * @link: true if the link is up.
* @an_enabled: true if autonegotiation is enabled/desired. * @an_enabled: true if autonegotiation is enabled/desired.
* @an_complete: true if autonegotiation has completed. * @an_complete: true if autonegotiation has completed.
...@@ -99,6 +103,7 @@ struct phylink_link_state { ...@@ -99,6 +103,7 @@ struct phylink_link_state {
int speed; int speed;
int duplex; int duplex;
int pause; int pause;
int rate_matching;
unsigned int link:1; unsigned int link:1;
unsigned int an_enabled:1; unsigned int an_enabled:1;
unsigned int an_complete:1; unsigned int an_complete:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment