summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2017-01-03 18:34:17 +0000
committerRussell King <rmk+kernel@armlinux.org.uk>2017-01-05 22:38:21 +0000
commite9ee6b1b5a50375c724bb4a12daae03a9df99926 (patch)
tree973cf0bea2aedc575ec78dac40014663045561fb
parentdf66255fb56f0ab1c56f5b0b4c1e43947104e804 (diff)
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 <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/net/phy/phylink.c40
-rw-r--r--include/linux/phylink.h1
2 files changed, 30 insertions, 11 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 6cac5ffb2a2c..aa653e35d5a8 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -242,8 +242,9 @@ static void phylink_mac_config(struct phylink *pl,
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 phylink *pl, struct phylink_link_state *
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_struct *w)
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_struct *w)
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 net_device *ndev, struct device_node *np,
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_device *phydev, bool up, bool do_carrier)
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));
}
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 097a97a669ba..9195c444aec5 100644
--- 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;