diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index da0623d94a64..619ef553476f 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -160,16 +160,93 @@ static const char *phylink_an_mode_str(unsigned int mode)
  * @state: A link state
  *
  * Update the .speed and .duplex members of @state. We can determine them based
- * on the .link_speed and .link_duplex. This function should be called whenever
- * .link_speed and .link_duplex are updated.  For example, userspace deals with
- * link speed and duplex, and not the interface speed and duplex. Similarly,
- * phys deal with link speed and duplex and only implicitly the interface speed
- * and duplex.
+ * on the .link_speed, .link_duplex, .interface, and .rate_adaptation. This
+ * function should be called whenever .link_speed and .link_duplex are updated.
+ * For example, userspace deals with link speed and duplex, and not the
+ * interface speed and duplex. Similarly, phys deal with link speed and duplex
+ * and only implicitly the interface speed and duplex.
  */
 static void phylink_state_fill_speed_duplex(struct phylink_link_state *state)
 {
-	state->speed = state->link_speed;
-	state->duplex = state->link_duplex;
+	switch (state->rate_adaptation) {
+	case RATE_ADAPT_NONE:
+		state->speed = state->link_speed;
+		state->duplex = state->link_duplex;
+		return;
+	case RATE_ADAPT_PAUSE:
+		state->duplex = DUPLEX_FULL;
+		break;
+	case RATE_ADAPT_CRS:
+		state->duplex = DUPLEX_HALF;
+		break;
+	case RATE_ADAPT_OPEN_LOOP:
+		state->duplex = state->link_duplex;
+		break;
+	}
+
+	/* Use the max speed of the interface */
+	switch (state->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:
+		state->speed = SPEED_100;
+		return;
+
+	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:
+		state->speed = SPEED_1000;
+		return;
+
+	case PHY_INTERFACE_MODE_2500BASEX:
+		state->speed = SPEED_2500;
+		return;
+
+	case PHY_INTERFACE_MODE_5GBASER:
+		state->speed = SPEED_5000;
+		return;
+
+	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:
+		state->speed = SPEED_10000;
+		return;
+
+	case PHY_INTERFACE_MODE_25GBASER:
+		state->speed = SPEED_25000;
+		return;
+
+	case PHY_INTERFACE_MODE_XLGMII:
+		state->speed = SPEED_40000;
+		return;
+
+	case PHY_INTERFACE_MODE_INTERNAL:
+		state->speed = state->link_speed;
+		return;
+
+	case PHY_INTERFACE_MODE_NA:
+	case PHY_INTERFACE_MODE_MAX:
+		state->speed = SPEED_UNKNOWN;
+		return;
+	}
+
+	WARN_ON(1);
 }
 
 /**
@@ -803,11 +880,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_adaptation_to_str(state->rate_adaptation),
 		    __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
 		    state->pause, state->link, state->an_enabled);
 
@@ -944,6 +1022,7 @@ 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;
+	state->rate_adaptation = pl->link_config.rate_adaptation;
 	if (state->an_enabled) {
 		state->link_speed = SPEED_UNKNOWN;
 		state->link_duplex = DUPLEX_UNKNOWN;
@@ -968,8 +1047,10 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
 	else
 		state->link = 0;
 
-	state->link_speed = state->speed;
-	state->link_duplex = state->duplex;
+	if (state->rate_adaptation == RATE_ADAPT_NONE) {
+		state->link_speed = state->speed;
+		state->link_duplex = state->duplex;
+	}
 }
 
 /* The fixed state is... fixed except for the link state,
@@ -1043,6 +1124,8 @@ static void phylink_link_up(struct phylink *pl,
 	struct net_device *ndev = pl->netdev;
 
 	pl->cur_interface = link_state.interface;
+	if (link_state.rate_adaptation == RATE_ADAPT_PAUSE)
+		link_state.pause |= MLO_PAUSE_RX;
 
 	if (pl->pcs && pl->pcs->ops->pcs_link_up)
 		pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
@@ -1145,6 +1228,18 @@ static void phylink_resolve(struct work_struct *w)
 				}
 				link_state.interface = pl->phy_state.interface;
 
+				/* If we are doing rate adaptation, then the
+				 * link speed/duplex comes from the PHY
+				 */
+				if (pl->phy_state.rate_adaptation) {
+					link_state.rate_adaptation =
+						pl->phy_state.rate_adaptation;
+					link_state.link_speed =
+						pl->phy_state.link_speed;
+					link_state.link_duplex =
+						pl->phy_state.link_duplex;
+				}
+
 				/* If we have a PHY, we need to update with
 				 * the PHY flow control bits.
 				 */
@@ -1380,6 +1475,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
 	mutex_lock(&pl->state_mutex);
 	pl->phy_state.link_speed = phydev->speed;
 	pl->phy_state.link_duplex = phydev->duplex;
+	pl->phy_state.rate_adaptation = phydev->rate_adaptation;
 	pl->phy_state.pause = MLO_PAUSE_NONE;
 	if (tx_pause)
 		pl->phy_state.pause |= MLO_PAUSE_TX;
@@ -1392,10 +1488,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_adaptation_to_str(phydev->rate_adaptation),
 		    phylink_pause_to_str(pl->phy_state.pause));
 }
 
@@ -1459,6 +1556,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
 	pl->phy_state.pause = MLO_PAUSE_NONE;
 	pl->phy_state.link_speed = SPEED_UNKNOWN;
 	pl->phy_state.link_duplex = DUPLEX_UNKNOWN;
+	pl->phy_state.rate_adaptation = RATE_ADAPT_NONE;
 	phylink_state_fill_speed_duplex(&pl->phy_state);
 	linkmode_copy(pl->supported, supported);
 	linkmode_copy(pl->link_config.advertising, config.advertising);
@@ -1902,8 +2000,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->link_speed;
-	kset->base.duplex = state->link_duplex;
+	if (kset->base.rate_adaptation == RATE_ADAPT_NONE) {
+		kset->base.speed = state->link_speed;
+		kset->base.duplex = state->link_duplex;
+	}
 	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
 				AUTONEG_DISABLE;
 }
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index ab5edc1e5330..f32b97f27ddc 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -80,6 +80,7 @@ struct phylink_link_state {
 	int duplex;
 	int link_duplex;
 	int pause;
+	int rate_adaptation;
 	unsigned int link:1;
 	unsigned int an_enabled:1;
 	unsigned int an_complete:1;
