diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
index 5136275c8e739..77fbc7eaadf51 100644
--- a/drivers/net/phy/mdio-bitbang.c
+++ b/drivers/net/phy/mdio-bitbang.c
@@ -149,16 +149,12 @@ static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr)
 	return dev_addr;
 }
 
-static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
+static int mdiobb_ll_read(struct mii_bus *bus, int op, int phy, int reg)
 {
 	struct mdiobb_ctrl *ctrl = bus->priv;
 	int ret, i;
 
-	if (reg & MII_ADDR_C45) {
-		reg = mdiobb_cmd_addr(ctrl, phy, reg);
-		mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
-	} else
-		mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
+	mdiobb_cmd(ctrl, op, phy, reg);
 
 	ctrl->ops->set_mdio_dir(ctrl, 0);
 
@@ -181,15 +177,25 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
 	return ret;
 }
 
-static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
+static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
 {
 	struct mdiobb_ctrl *ctrl = bus->priv;
+	int op = MDIO_READ;
 
 	if (reg & MII_ADDR_C45) {
 		reg = mdiobb_cmd_addr(ctrl, phy, reg);
-		mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
-	} else
-		mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
+		op = MDIO_C45_READ;
+	}
+
+	return mdiobb_ll_read(bus, op, phy, reg);
+}
+
+static int mdiobb_ll_write(struct mii_bus *bus, int op, int phy,
+			   int reg, u16 val)
+{
+	struct mdiobb_ctrl *ctrl = bus->priv;
+
+	mdiobb_cmd(ctrl, op, phy, reg);
 
 	/* send the turnaround (10) */
 	mdiobb_send_bit(ctrl, 1);
@@ -202,6 +208,19 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
 	return 0;
 }
 
+static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+	struct mdiobb_ctrl *ctrl = bus->priv;
+	int op = MDIO_WRITE;
+
+	if (reg & MII_ADDR_C45) {
+		reg = mdiobb_cmd_addr(ctrl, phy, reg);
+		op = MDIO_C45_WRITE;
+	}
+
+	return mdiobb_ll_write(bus, op, phy, reg, val);
+}
+
 struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
 {
 	struct mii_bus *bus;
@@ -213,7 +232,9 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
 	__module_get(ctrl->ops->owner);
 
 	bus->read = mdiobb_read;
+	bus->ll_read = mdiobb_ll_read;
 	bus->write = mdiobb_write;
+	bus->ll_write = mdiobb_ll_write;
 	bus->priv = ctrl;
 
 	return bus;
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 229e480179ff1..57f4b7b9ce39a 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -560,6 +560,34 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
 }
 EXPORT_SYMBOL(__mdiobus_read);
 
+/**
+ * __mdiobus_ll_read - Unlocked version of the mdiobus_read function
+ * @bus: the mii_bus struct
+ * @op: opcode to use on transfer
+ * @addr: the phy address
+ * @regnum: register number to read
+ *
+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
+ *
+ * NOTE: MUST NOT be called from interrupt context.
+ */
+int __mdiobus_ll_read(struct mii_bus *bus, int op, int addr, u32 regnum)
+{
+	int retval;
+
+	if (!bus->ll_write)
+		return -ENODEV;
+
+	WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+
+	retval = bus->ll_read(bus, op, addr, regnum);
+
+	trace_mdio_access(bus, 1, addr, regnum, retval, retval);
+
+	return retval;
+}
+EXPORT_SYMBOL(__mdiobus_ll_read);
+
 /**
  * __mdiobus_write - Unlocked version of the mdiobus_write function
  * @bus: the mii_bus struct
@@ -585,6 +613,36 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
 }
 EXPORT_SYMBOL(__mdiobus_write);
 
+/**
+ * __mdiobus_ll_write - Unlocked version of the mdiobus_write function
+ * @bus: the mii_bus struct
+ * @op: opcode to use on transfer
+ * @addr: the phy address
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ *
+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
+ *
+ * NOTE: MUST NOT be called from interrupt context.
+ */
+int __mdiobus_ll_write(struct mii_bus *bus, int op, int addr,
+		       u32 regnum, u16 val)
+{
+	int err;
+
+	if (!bus->ll_write)
+		return -ENODEV;
+
+	WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+
+	err = bus->ll_write(bus, op, addr, regnum, val);
+
+	trace_mdio_access(bus, 0, addr, regnum, val, err);
+
+	return err;
+}
+EXPORT_SYMBOL(__mdiobus_ll_write);
+
 /**
  * mdiobus_read_nested - Nested version of the mdiobus_read function
  * @bus: the mii_bus struct
@@ -636,6 +694,31 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
 }
 EXPORT_SYMBOL(mdiobus_read);
 
+/**
+ * mdiobus_ll_read - Convenience function for reading a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @op: opcode to use on transfer
+ * @addr: the phy address
+ * @regnum: register number to read
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int mdiobus_ll_read(struct mii_bus *bus, int op, int addr, u32 regnum)
+{
+	int retval;
+
+	BUG_ON(in_interrupt());
+
+	mutex_lock(&bus->mdio_lock);
+	retval = __mdiobus_ll_read(bus, op, addr, regnum);
+	mutex_unlock(&bus->mdio_lock);
+
+	return retval;
+}
+EXPORT_SYMBOL(mdiobus_ll_read);
+
 /**
  * mdiobus_write_nested - Nested version of the mdiobus_write function
  * @bus: the mii_bus struct
@@ -689,6 +772,33 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
 }
 EXPORT_SYMBOL(mdiobus_write);
 
+/**
+ * mdiobus_ll_write - Convenience function for writing a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @op: opcode to use on transfer
+ * @addr: the phy address
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int mdiobus_ll_write(struct mii_bus *bus, int op, int addr,
+		     u32 regnum, u16 val)
+{
+	int err;
+
+	BUG_ON(in_interrupt());
+
+	mutex_lock(&bus->mdio_lock);
+	err = __mdiobus_ll_write(bus, op, addr, regnum, val);
+	mutex_unlock(&bus->mdio_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(mdiobus_ll_write);
+
 /**
  * mdio_bus_match - determine if given MDIO driver supports the given
  *		    MDIO device
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index a7604248777b7..aafd24eb6d393 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -315,11 +315,17 @@ static inline void mii_10gbt_stat_mod_linkmode_lpa_t(unsigned long *advertising,
 }
 
 int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int __mdiobus_ll_read(struct mii_bus *bus, int op, int addr, u32 regnum);
 int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+int __mdiobus_ll_write(struct mii_bus *bus, int op, int addr,
+		       u32 regnum, u16 val);
 
 int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_ll_read(struct mii_bus *bus, int op, int addr, u32 regnum);
 int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
 int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+int mdiobus_ll_write(struct mii_bus *bus, int op, int addr,
+		     u32 regnum, u16 val);
 int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
 
 int mdiobus_register_device(struct mdio_device *mdiodev);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5032d453ac66a..3bb802cb03a8a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -218,6 +218,9 @@ struct mii_bus {
 	void *priv;
 	int (*read)(struct mii_bus *bus, int addr, int regnum);
 	int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
+	int (*ll_read)(struct mii_bus *bus, int op, int addr, int regnum);
+	int (*ll_write)(struct mii_bus *bus, int op, int addr,
+			int regnum, u16 val);
 	int (*reset)(struct mii_bus *bus);
 
 	/*
