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-05-05 18:42:38 +0100
commit9f4bfb85c85851e88ccba1f2de928b316061fa73 (patch)
tree50276ed4158c44486ca8057f44614aa93668e385
parent6e29f0087c0d7599ff945656dd878802f1f5136d (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 8afc695fe7e5..fc0c6eca9101 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -271,8 +271,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,
@@ -293,6 +294,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;
@@ -373,19 +375,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;
@@ -401,13 +422,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);
@@ -443,8 +457,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;
@@ -500,12 +516,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 1a5968267834..41bebd44e767 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -32,6 +32,7 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
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;