From: Russell King <rmk+kernel@armlinux.org.uk>
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 <rmk+kernel@armlinux.org.uk>
---

--- 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;
