diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 5f73d27fe330..57062574ca04 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -575,26 +575,34 @@ EXPORT_SYMBOL(__phy_mmd_indirect);
  */
 int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
 {
-	int val;
+	struct mii_bus *bus = phydev->mdio.bus;
+	int phy_addr = phydev->mdio.addr;
+	bool check_rc = true;
+	int ret;
 
 	if (regnum > (u16)~0 || devad > 32)
 		return -EINVAL;
 
-	if (phydev->drv && phydev->drv->read_mmd) {
-		val = phydev->drv->read_mmd(phydev, devad, regnum);
-	} else if (phy_is_c45(phydev)) {
-		val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
-					 devad, regnum);
-	} else {
-		struct mii_bus *bus = phydev->mdio.bus;
-		int phy_addr = phydev->mdio.addr;
-
-		mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+	if (phydev->drv && phydev->drv->read_mmd)
+		return phydev->drv->read_mmd(phydev, devad, regnum);
+
+	switch (phydev->access_mode) {
+	case PHY_ACCESS_C45:
+		return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
+	case PHY_ACCESS_C22:
+		/* ignore return value for legacy reasons */
+		check_rc = false;
+		fallthrough;
+	case PHY_ACCESS_C45_OVER_C22:
+		ret = mmd_phy_indirect(bus, phy_addr, devad, regnum, check_rc);
+		if (check_rc && ret)
+			return ret;
 
 		/* Read the content of the MMD's selected register */
-		val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
+		return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
 	}
-	return val;
+
+	return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(__phy_read_mmd);
 
@@ -631,28 +639,35 @@ EXPORT_SYMBOL(phy_read_mmd);
  */
 int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
 {
+	struct mii_bus *bus = phydev->mdio.bus;
+	int phy_addr = phydev->mdio.addr;
+	bool check_rc = true;
 	int ret;
 
 	if (regnum > (u16)~0 || devad > 32)
 		return -EINVAL;
 
-	if (phydev->drv && phydev->drv->write_mmd) {
-		ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
-	} else if (phy_is_c45(phydev)) {
-		ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
-					  devad, regnum, val);
-	} else {
-		struct mii_bus *bus = phydev->mdio.bus;
-		int phy_addr = phydev->mdio.addr;
-
-		mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+	if (phydev->drv && phydev->drv->write_mmd)
+		return phydev->drv->write_mmd(phydev, devad, regnum, val);
+
+	switch (phydev->access_mode) {
+	case PHY_ACCESS_C45:
+		return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
+	case PHY_ACCESS_C22:
+		check_rc = false;
+		fallthrough;
+	case PHY_ACCESS_C45_OVER_C22:
+		ret = mmd_phy_indirect(bus, phy_addr, devad, regnum, check_rc);
+		if (check_rc && ret)
+			return ret;
 
 		/* Write the data into MMD's selected register */
-		__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
+		ret = __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
 
-		ret = 0;
+		return check_rc ? ret : 0;
 	}
-	return ret;
+
+	return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(__phy_write_mmd);
 
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 4fd095282ef7..13d5ec7a7c21 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -715,6 +715,28 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
 }
 EXPORT_SYMBOL(phy_device_create);
 
+static int phy_probe_mmd_read(struct mii_bus *bus, int prtad, int devad,
+			      u16 regnum, bool c45_over_c22)
+{
+	int ret;
+
+	if (!c45_over_c22)
+		return mdiobus_c45_read(bus, prtad, devad, regnum);
+
+	mutex_lock(&bus->mdio_lock);
+
+	ret = __phy_mmd_indirect(bus, prtad, devad, regnum);
+	if (ret)
+		goto out;
+
+	ret = __mdiobus_read(bus, prtad, MII_MMD_DATA);
+
+out:
+	mutex_unlock(&bus->mdio_lock);
+
+	return ret;
+}
+
 /* phy_c45_probe_present - checks to see if a MMD is present in the package
  * @bus: the target MII bus
  * @prtad: PHY package address on the MII bus
@@ -726,11 +748,13 @@ EXPORT_SYMBOL(phy_device_create);
  * Returns: negative error number on bus access error, zero if no device
  * is responding, or positive if a device is present.
  */
-static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
+static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad,
+				 bool c45_over_c22)
 {
 	int stat2;
 
-	stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+	stat2 = phy_probe_mmd_read(bus, prtad, devad, MDIO_STAT2,
+				   c45_over_c22);
 	if (stat2 < 0)
 		return stat2;
 
@@ -749,16 +773,18 @@ static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
  * Returns: 0 on success, -EIO on failure.
  */
 static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
-				   u32 *devices_in_package)
+				   u32 *devices_in_package, bool c45_over_c22)
 {
 	int phy_reg;
 
-	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+	phy_reg = phy_probe_mmd_read(bus, addr, dev_addr, MDIO_DEVS2,
+				     c45_over_c22);
 	if (phy_reg < 0)
 		return -EIO;
 	*devices_in_package = phy_reg << 16;
 
-	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1);
+	phy_reg = phy_probe_mmd_read(bus, addr, dev_addr, MDIO_DEVS1,
+				     c45_over_c22);
 	if (phy_reg < 0)
 		return -EIO;
 	*devices_in_package |= phy_reg;
@@ -780,7 +806,8 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
  * the "devices in package" is invalid.
  */
 static int get_phy_c45_ids(struct mii_bus *bus, int addr,
-			   struct phy_c45_device_ids *c45_ids)
+			   struct phy_c45_device_ids *c45_ids,
+			   bool c45_over_c22)
 {
 	const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
 	u32 devs_in_pkg = 0;
@@ -798,14 +825,16 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
 			 * Some PHYs (88x3310) vendor space is not IEEE802.3
 			 * compliant.
 			 */
-			ret = phy_c45_probe_present(bus, addr, i);
+			ret = phy_c45_probe_present(bus, addr, i,
+						    c45_over_c22);
 			if (ret < 0)
 				return -EIO;
 
 			if (!ret)
 				continue;
 		}
-		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg,
+						  c45_over_c22);
 		if (phy_reg < 0)
 			return -EIO;
 	}
@@ -815,7 +844,8 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
 		 * MMD 0, as some 10G PHYs have zero Devices In package,
 		 * e.g. Cortina CS4315/CS4340 PHY.
 		 */
-		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg);
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg,
+						  c45_over_c22);
 		if (phy_reg < 0)
 			return -EIO;
 
@@ -834,7 +864,8 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
 			 * to ignore these if they do not contain IEEE 802.3
 			 * registers.
 			 */
-			ret = phy_c45_probe_present(bus, addr, i);
+			ret = phy_c45_probe_present(bus, addr, i,
+						    c45_over_c22);
 			if (ret < 0)
 				return ret;
 
@@ -842,12 +873,14 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
 				continue;
 		}
 
-		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
+		phy_reg = phy_probe_mmd_read(bus, addr, i, MII_PHYSID1,
+					     c45_over_c22);
 		if (phy_reg < 0)
 			return -EIO;
 		c45_ids->device_ids[i] = phy_reg << 16;
 
-		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
+		phy_reg = phy_probe_mmd_read(bus, addr, i, MII_PHYSID2,
+					     c45_over_c22);
 		if (phy_reg < 0)
 			return -EIO;
 		c45_ids->device_ids[i] |= phy_reg;
@@ -961,7 +994,10 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr,
 		r = get_phy_c22_id(bus, addr, &phy_id);
 		break;
 	case PHY_ACCESS_C45:
-		r = get_phy_c45_ids(bus, addr, &c45_ids);
+		r = get_phy_c45_ids(bus, addr, &c45_ids, false);
+		break;
+	case PHY_ACCESS_C45_OVER_C22:
+		r = get_phy_c45_ids(bus, addr, &c45_ids, true);
 		break;
 	default:
 		return ERR_PTR(-EIO);
@@ -976,7 +1012,7 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr,
 	 * space, if successful, create the C45 PHY device.
 	 */
 	if (mode == PHY_ACCESS_C22 && phy_id == 0 && bus->read_c45) {
-		r = get_phy_c45_ids(bus, addr, &c45_ids);
+		r = get_phy_c45_ids(bus, addr, &c45_ids, false);
 		if (!r)
 			return phy_device_create(bus, addr, phy_id,
 						 PHY_ACCESS_C45, &c45_ids);
@@ -1058,7 +1094,7 @@ EXPORT_SYMBOL(phy_device_remove);
 int phy_get_c45_ids(struct phy_device *phydev)
 {
 	return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
-			       &phydev->c45_ids);
+			       &phydev->c45_ids, false);
 }
 EXPORT_SYMBOL(phy_get_c45_ids);
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index a7aff91f4eb0..4852651f6326 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -541,10 +541,13 @@ struct macsec_ops;
  *
  * @PHY_ACCESS_C22: use 802.3 c22 MDIO transactions
  * @PHY_ACCESS_C45: use 802.3 c45 MDIO transactions
+ * @PHY_ACCESS_C45_OVER_C22: indirectly access C45 registers by using by 802.3
+ *			     c22 MDIO transactions and registers 13 and 14.
  */
 enum phy_access_mode {
 	PHY_ACCESS_C22,
 	PHY_ACCESS_C45,
+	PHY_ACCESS_C45_OVER_C22,
 };
 
 /**
