diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index b08716fe22c1..a952cdc7c96e 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -296,6 +296,75 @@ static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
 	}
 }
 
+/**
+ * phy_interface_speed() - get the speed of a phy interface
+ * @interface: phy interface mode defined by &typedef phy_interface_t
+ * @link_speed: the speed of the link
+ *
+ * Some phy interfaces modes adapt to the speed of the underlying link (such as
+ * by duplicating data or changing the clock rate). Others, however, are fixed
+ * at a particular rate. Determine the speed of a phy interface mode for a
+ * particular link speed.
+ *
+ * Return: The speed of @interface
+ */
+static int phy_interface_speed(phy_interface_t interface, int link_speed)
+{
+	switch (interface) {
+	case PHY_INTERFACE_MODE_100BASEX:
+		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:
+		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:
+		return SPEED_10000;
+
+	case PHY_INTERFACE_MODE_25GBASER:
+		return SPEED_25000;
+
+	case PHY_INTERFACE_MODE_XLGMII:
+		return SPEED_40000;
+
+	case PHY_INTERFACE_MODE_USXGMII:
+	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:
+	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:
+	case PHY_INTERFACE_MODE_INTERNAL:
+		return link_speed;
+
+	case PHY_INTERFACE_MODE_NA:
+	case PHY_INTERFACE_MODE_MAX:
+		break;
+	}
+
+	return SPEED_UNKNOWN;
+}
+
 /**
  * phylink_get_linkmodes() - get acceptable link modes
  * @linkmodes: ethtool linkmode mask (must be already initialised)
@@ -515,7 +584,7 @@ static int phylink_parse_fixedlink(struct phylink *pl,
 	if (fixed_node) {
 		ret = fwnode_property_read_u32(fixed_node, "speed", &speed);
 
-		pl->link_config.speed = speed;
+		pl->link_config.link_speed = speed;
 		pl->link_config.duplex = DUPLEX_HALF;
 
 		if (fwnode_property_read_bool(fixed_node, "full-duplex"))
@@ -559,7 +628,7 @@ static int phylink_parse_fixedlink(struct phylink *pl,
 		if (!ret) {
 			pl->link_config.duplex = prop[1] ?
 						DUPLEX_FULL : DUPLEX_HALF;
-			pl->link_config.speed = prop[2];
+			pl->link_config.link_speed = prop[2];
 			if (prop[3])
 				__set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
 					  pl->link_config.lp_advertising);
@@ -569,11 +638,13 @@ static int phylink_parse_fixedlink(struct phylink *pl,
 		}
 	}
 
-	if (pl->link_config.speed > SPEED_1000 &&
+	if (pl->link_config.link_speed > SPEED_1000 &&
 	    pl->link_config.duplex != DUPLEX_FULL)
 		phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n",
-			     pl->link_config.speed);
+			     pl->link_config.link_speed);
 
+	pl->link_config.speed = phy_interface_speed(pl->link_config.interface,
+						    pl->link_config.link_speed);
 	bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
 	linkmode_copy(pl->link_config.advertising, pl->supported);
 	phylink_validate(pl, pl->supported, &pl->link_config);
@@ -1270,7 +1341,8 @@ struct phylink *phylink_create(struct phylink_config *config,
 		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.link_speed = SPEED_UNKNOWN;
+	pl->link_config.speed = phy_interface_speed(iface, SPEED_UNKNOWN);
 	pl->link_config.duplex = DUPLEX_UNKNOWN;
 	pl->link_config.an_enabled = true;
 	pl->mac_ops = mac_ops;
@@ -1335,7 +1407,9 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
 	phy_get_pause(phydev, &tx_pause, &rx_pause);
 
 	mutex_lock(&pl->state_mutex);
-	pl->phy_state.speed = phydev->speed;
+	pl->phy_state.link_speed = phydev->speed;
+	pl->phy_state.speed = phy_interface_speed(phydev->interface,
+						  phydev->speed);
 	pl->phy_state.duplex = phydev->duplex;
 	pl->phy_state.pause = MLO_PAUSE_NONE;
 	if (tx_pause)
@@ -1413,7 +1487,8 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
 	pl->phydev = phy;
 	pl->phy_state.interface = interface;
 	pl->phy_state.pause = MLO_PAUSE_NONE;
-	pl->phy_state.speed = SPEED_UNKNOWN;
+	pl->phy_state.link_speed = SPEED_UNKNOWN;
+	pl->phy_state.speed = phy_interface_speed(interface, SPEED_UNKNOWN);
 	pl->phy_state.duplex = DUPLEX_UNKNOWN;
 	linkmode_copy(pl->supported, supported);
 	linkmode_copy(pl->link_config.advertising, config.advertising);
@@ -1857,7 +1932,7 @@ 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->speed;
+	kset->base.speed = state->link_speed;
 	kset->base.duplex = state->duplex;
 	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
 				AUTONEG_DISABLE;
@@ -1974,13 +2049,13 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 		 * If the link parameters match, accept them but do nothing.
 		 */
 		if (pl->cur_link_an_mode == MLO_AN_FIXED) {
-			if (s->speed != pl->link_config.speed ||
+			if (s->speed != pl->link_config.link_speed ||
 			    s->duplex != pl->link_config.duplex)
 				return -EINVAL;
 			return 0;
 		}
 
-		config.speed = s->speed;
+		config.link_speed = s->speed;
 		config.duplex = s->duplex;
 		break;
 
@@ -1996,7 +2071,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 			return 0;
 		}
 
-		config.speed = SPEED_UNKNOWN;
+		config.link_speed = SPEED_UNKNOWN;
 		config.duplex = DUPLEX_UNKNOWN;
 		break;
 
@@ -2046,7 +2121,10 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
 	if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
 		return -EINVAL;
 
+	config.speed = phy_interface_speed(config.interface, config.link_speed);
+
 	mutex_lock(&pl->state_mutex);
+	pl->link_config.link_speed = config.link_speed;
 	pl->link_config.speed = config.speed;
 	pl->link_config.duplex = config.duplex;
 	pl->link_config.an_enabled = config.an_enabled;
@@ -2291,7 +2369,7 @@ static int phylink_mii_emul_read(unsigned int reg,
 	int val;
 
 	fs.link = state->link;
-	fs.speed = state->speed;
+	fs.speed = state->link_speed;
 	fs.duplex = state->duplex;
 	fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa);
 	fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa);
@@ -2588,7 +2666,8 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,
 	memset(&config, 0, sizeof(config));
 	linkmode_copy(config.advertising, advertising);
 	config.interface = PHY_INTERFACE_MODE_NA;
-	config.speed = SPEED_UNKNOWN;
+	config.link_speed = SPEED_UNKNOWN;
+	config.speed = PHY_INTERFACE_MODE_NA;
 	config.duplex = DUPLEX_UNKNOWN;
 	config.pause = MLO_PAUSE_AN;
 	config.an_enabled = pl->link_config.an_enabled;
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 6d06896fc20d..30e3fbe19fb4 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -56,7 +56,10 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
  * @lp_advertising: ethtool bitmask containing link partner advertised link
  *   modes
  * @interface: link &typedef phy_interface_t mode
- * @speed: link speed, one of the SPEED_* constants.
+ * @speed: interface speed, one of the SPEED_* constants. If
+ *   @rate_adaptation is being performed, this will be different from
+ *   @link_speed.
+ * @link_speed: link speed, one of the SPEED_* constants.
  * @duplex: link duplex mode, one of DUPLEX_* constants.
  * @pause: link pause state, described by MLO_PAUSE_* constants.
  * @link: true if the link is up.
@@ -68,6 +71,7 @@ struct phylink_link_state {
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
 	phy_interface_t interface;
 	int speed;
+	int link_speed;
 	int duplex;
 	int pause;
 	unsigned int link:1;
