diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index ea3772618043..20f979ef1faf 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -26,6 +26,7 @@
 #include <linux/of_device.h>
 #include <linux/of_mdio.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/sys_soc.h>
 
 /*
  * This timeout definition is a worst-case ultra defensive measure against
@@ -39,8 +40,16 @@
 
 #define DEF_OUT_FREQ		2200000		/* 2.2 MHz */
 
+#define MDIO_BITRANGE		0x8000
+#define C22_READ_PATTERN	0x6
+#define C22_WRITE_PATTERN	0x5
+#define C22_BITRANGE		0x8
+#define PHY_BITRANGE		0x10
+#define PHY_DATA_BITRANGE	0x8000
+
 struct davinci_mdio_of_param {
 	int autosuspend_delay_ms;
+	bool manual_mode;
 };
 
 struct davinci_mdio_regs {
@@ -49,6 +58,12 @@ struct davinci_mdio_regs {
 #define CONTROL_IDLE		BIT(31)
 #define CONTROL_ENABLE		BIT(30)
 #define CONTROL_MAX_DIV		(0xffff)
+#define CONTROL_CLKDIV		GENMASK(15, 0)
+
+#define MDIO_MAN_MDCLK_O	BIT(2)
+#define MDIO_MAN_OE		BIT(1)
+#define MDIO_MAN_PIN		BIT(0)
+#define MDIO_MANUALMODE		BIT(31)
 
 	u32	alive;
 	u32	link;
@@ -59,7 +74,9 @@ struct davinci_mdio_regs {
 	u32	userintmasked;
 	u32	userintmaskset;
 	u32	userintmaskclr;
-	u32	__reserved_1[20];
+	u32	manualif;
+	u32	poll;
+	u32	__reserved_1[18];
 
 	struct {
 		u32	access;
@@ -73,6 +90,11 @@ struct davinci_mdio_regs {
 	}	user[];
 };
 
+enum davinci_mdio_manual {
+	MDIO_PIN = 0,
+	MDIO_OE,
+	MDIO_MDCLK,
+};
 static const struct mdio_platform_data default_pdata = {
 	.bus_freq = DEF_OUT_FREQ,
 };
@@ -90,6 +112,8 @@ struct davinci_mdio_data {
 	 */
 	bool		skip_scan;
 	u32		clk_div;
+	u32		mdio_manualif;
+	bool		manual_mode;
 };
 
 static void davinci_mdio_init_clk(struct davinci_mdio_data *data)
@@ -122,12 +146,257 @@ static void davinci_mdio_init_clk(struct davinci_mdio_data *data)
 		data->access_time = 1;
 }
 
+static void davinci_mdio_disable(struct davinci_mdio_data *data)
+{
+	u32 reg;
+
+	/* Disable MDIO state machine */
+	reg = readl(&data->regs->control);
+
+	reg &= ~CONTROL_CLKDIV;
+	reg |= data->clk_div;
+
+	reg &= ~CONTROL_ENABLE;
+	writel(reg, &data->regs->control);
+}
+
+static void davinci_mdio_enable_manual_mode(struct davinci_mdio_data *data)
+{
+	u32 reg;
+	/* set manual mode */
+	reg = readl(&data->regs->poll);
+	reg |= MDIO_MANUALMODE;
+	writel(reg, &data->regs->poll);
+
+	data->mdio_manualif = readl(&data->regs->manualif);
+}
+
 static void davinci_mdio_enable(struct davinci_mdio_data *data)
 {
 	/* set enable and clock divider */
 	writel(data->clk_div | CONTROL_ENABLE, &data->regs->control);
 }
 
+static void davinci_mdio_sw_set_bit(struct davinci_mdio_data *data,
+				    enum davinci_mdio_manual bit)
+{
+	u32 man_reg;
+
+	switch (bit) {
+	case MDIO_OE:
+		data->mdio_manualif |= MDIO_MAN_OE;
+		writel(data->mdio_manualif, &data->regs->manualif);
+		break;
+	case MDIO_PIN:
+		data->mdio_manualif |= MDIO_MAN_PIN;
+		writel(data->mdio_manualif, &data->regs->manualif);
+		break;
+	case MDIO_MDCLK:
+		man_reg = readl(&data->regs->manualif);
+		man_reg |= MDIO_MAN_MDCLK_O;
+		writel(man_reg, &data->regs->manualif);
+		data->mdio_manualif = readl(&data->regs->manualif);
+		break;
+	default:
+		break;
+	};
+}
+
+static void davinci_mdio_sw_clr_bit(struct davinci_mdio_data *data,
+				    enum davinci_mdio_manual bit)
+{
+	u32 man_reg;
+
+	switch (bit) {
+	case MDIO_OE:
+		data->mdio_manualif &= ~MDIO_MAN_OE;
+		writel(data->mdio_manualif, &data->regs->manualif);
+		break;
+	case MDIO_PIN:
+		data->mdio_manualif &= ~MDIO_MAN_PIN;
+		writel(data->mdio_manualif, &data->regs->manualif);
+		break;
+	case MDIO_MDCLK:
+		man_reg = readl(&data->regs->manualif);
+		man_reg &= ~MDIO_MAN_MDCLK_O;
+		writel(man_reg, &data->regs->manualif);
+		data->mdio_manualif = readl(&data->regs->manualif);
+		break;
+	default:
+		break;
+	};
+}
+
+static int davinci_mdio_test_man_bit(struct davinci_mdio_data *data,
+				     enum davinci_mdio_manual bit)
+{
+	unsigned long reg;
+
+	reg = readl(&data->regs->manualif);
+	return test_bit(bit, &reg);
+}
+
+static void davinci_mdio_toggle_man_bit(struct davinci_mdio_data *data,
+					enum davinci_mdio_manual bit)
+{
+	davinci_mdio_sw_clr_bit(data, bit);
+	davinci_mdio_sw_set_bit(data, bit);
+}
+
+static void davinci_mdio_man_send_pattern(struct davinci_mdio_data *data,
+					  u32 bitrange, u32 val)
+{
+	u32 i;
+
+	for (i = bitrange; i; i = i >> 1) {
+		if (i & val)
+			davinci_mdio_sw_set_bit(data, MDIO_PIN);
+		else
+			davinci_mdio_sw_clr_bit(data, MDIO_PIN);
+
+		davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+	}
+}
+
+static void davinci_mdio_sw_preamble(struct davinci_mdio_data *data)
+{
+	u32 i;
+
+	davinci_mdio_sw_clr_bit(data, MDIO_OE);
+
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_set_bit(data, MDIO_MDCLK);
+
+	for (i = 0; i < 32; i++) {
+		davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+		davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+		davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+		davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+	}
+}
+
+static int davinci_mdio_sw_read(struct mii_bus *bus, int phy_id, int phy_reg)
+{
+	struct davinci_mdio_data *data = bus->priv;
+	u32 reg, i;
+	int ret;
+	u8 ack;
+
+	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+		return -EINVAL;
+
+	ret = pm_runtime_get_sync(data->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(data->dev);
+		return ret;
+	}
+
+	davinci_mdio_disable(data);
+	davinci_mdio_enable_manual_mode(data);
+	davinci_mdio_sw_preamble(data);
+
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_set_bit(data, MDIO_OE);
+
+	 /* Issue clause 22 MII read function {0,1,1,0} */
+	davinci_mdio_man_send_pattern(data, C22_BITRANGE, C22_READ_PATTERN);
+
+	/* Send the device number MSB first */
+	davinci_mdio_man_send_pattern(data, PHY_BITRANGE, phy_id);
+
+	/* Send the register number MSB first */
+	davinci_mdio_man_send_pattern(data, PHY_BITRANGE, phy_reg);
+
+	/* Send turn around cycles */
+	davinci_mdio_sw_clr_bit(data, MDIO_OE);
+
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+	ack = davinci_mdio_test_man_bit(data, MDIO_PIN);
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+	reg = 0;
+	if (ack == 0) {
+		for (i = MDIO_BITRANGE; i; i = i >> 1) {
+			if (davinci_mdio_test_man_bit(data, MDIO_PIN))
+				reg |= i;
+
+			davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+		}
+	} else {
+		for (i = MDIO_BITRANGE; i; i = i >> 1)
+			davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+		reg = 0xFFFF;
+	}
+
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_set_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_set_bit(data, MDIO_MDCLK);
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+	pm_runtime_mark_last_busy(data->dev);
+	pm_runtime_put_autosuspend(data->dev);
+
+	return reg;
+}
+
+static int davinci_mdio_sw_write(struct mii_bus *bus, int phy_id,
+				 int phy_reg, u16 phy_data)
+{
+	struct davinci_mdio_data *data = bus->priv;
+	int ret;
+
+	if ((phy_reg & ~PHY_REG_MASK) || (phy_id & ~PHY_ID_MASK))
+		return -EINVAL;
+
+	ret = pm_runtime_get_sync(data->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(data->dev);
+		return ret;
+	}
+
+	davinci_mdio_disable(data);
+	davinci_mdio_enable_manual_mode(data);
+	davinci_mdio_sw_preamble(data);
+
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_set_bit(data, MDIO_OE);
+
+	/* Issue clause 22 MII write function {0,1,0,1} */
+	davinci_mdio_man_send_pattern(data, C22_BITRANGE, C22_WRITE_PATTERN);
+
+	/* Send the device number MSB first */
+	davinci_mdio_man_send_pattern(data, PHY_BITRANGE, phy_id);
+
+	/* Send the register number MSB first */
+	davinci_mdio_man_send_pattern(data, PHY_BITRANGE, phy_reg);
+
+	/* set turn-around cycles */
+	davinci_mdio_sw_set_bit(data, MDIO_PIN);
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_clr_bit(data, MDIO_PIN);
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+	/* Send Register data MSB first */
+	davinci_mdio_man_send_pattern(data, PHY_DATA_BITRANGE, phy_data);
+	davinci_mdio_sw_clr_bit(data, MDIO_OE);
+
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_sw_clr_bit(data, MDIO_MDCLK);
+	davinci_mdio_toggle_man_bit(data, MDIO_MDCLK);
+
+	pm_runtime_mark_last_busy(data->dev);
+	pm_runtime_put_autosuspend(data->dev);
+
+	ret = 0;
+
+	return ret;
+}
+
 static int davinci_mdio_reset(struct mii_bus *bus)
 {
 	struct davinci_mdio_data *data = bus->priv;
@@ -138,6 +407,12 @@ static int davinci_mdio_reset(struct mii_bus *bus)
 	if (ret < 0)
 		return ret;
 
+	if (data->manual_mode) {
+		davinci_mdio_disable(data);
+		davinci_mdio_enable_manual_mode(data);
+		davinci_mdio_sw_preamble(data);
+	}
+
 	/* wait for scan logic to settle */
 	msleep(PHY_MAX_ADDR * data->access_time);
 
@@ -319,6 +594,28 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
 }
 
 #if IS_ENABLED(CONFIG_OF)
+struct k3_mdio_soc_data {
+	bool manual_mode;
+};
+
+static const struct k3_mdio_soc_data am65_mdio_soc_data = {
+	.manual_mode = true,
+};
+
+static const struct soc_device_attribute k3_mdio_socinfo[] = {
+	{ .family = "AM62X", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ .family = "AM64X", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ .family = "AM64X", .revision = "SR2.0", .data = &am65_mdio_soc_data },
+	{ .family = "AM65X", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ .family = "AM65X", .revision = "SR2.0", .data = &am65_mdio_soc_data },
+	{ .family = "J7200", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ .family = "J7200", .revision = "SR2.0", .data = &am65_mdio_soc_data },
+	{ .family = "J721E", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ .family = "J721E", .revision = "SR2.0", .data = &am65_mdio_soc_data },
+	{ .family = "J721S2", .revision = "SR1.0", .data = &am65_mdio_soc_data },
+	{ /* sentinel */ },
+};
+
 static const struct davinci_mdio_of_param of_cpsw_mdio_data = {
 	.autosuspend_delay_ms = 100,
 };
@@ -340,6 +637,7 @@ static int davinci_mdio_probe(struct platform_device *pdev)
 	struct phy_device *phy;
 	int ret, addr;
 	int autosuspend_delay_ms = -1;
+	const struct soc_device_attribute *soc_match_data;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -351,6 +649,8 @@ static int davinci_mdio_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
+	data->manual_mode = false;
+
 	if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
 		const struct davinci_mdio_of_param *of_mdio_data;
 
@@ -364,6 +664,15 @@ static int davinci_mdio_probe(struct platform_device *pdev)
 			autosuspend_delay_ms =
 					of_mdio_data->autosuspend_delay_ms;
 		}
+
+		soc_match_data = soc_device_match(k3_mdio_socinfo);
+		if (soc_match_data && soc_match_data->data) {
+			const struct k3_mdio_soc_data *socdata =
+						soc_match_data->data;
+
+			data->manual_mode = socdata->manual_mode;
+		}
+
 	} else {
 		data->pdata = pdata ? (*pdata) : default_pdata;
 		snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s-%x",
@@ -371,8 +680,16 @@ static int davinci_mdio_probe(struct platform_device *pdev)
 	}
 
 	data->bus->name		= dev_name(dev);
-	data->bus->read		= davinci_mdio_read;
-	data->bus->write	= davinci_mdio_write;
+
+	if (data->manual_mode) {
+		data->bus->read		= davinci_mdio_sw_read;
+		data->bus->write	= davinci_mdio_sw_write;
+		dev_info(dev, "Configuring MDIO in manual mode\n");
+	} else {
+		data->bus->read		= davinci_mdio_read;
+		data->bus->write	= davinci_mdio_write;
+	}
+
 	data->bus->reset	= davinci_mdio_reset;
 	data->bus->parent	= dev;
 	data->bus->priv		= data;
@@ -452,7 +769,9 @@ static int davinci_mdio_runtime_suspend(struct device *dev)
 	ctrl = readl(&data->regs->control);
 	ctrl &= ~CONTROL_ENABLE;
 	writel(ctrl, &data->regs->control);
-	wait_for_idle(data);
+
+	if (!data->manual_mode)
+		wait_for_idle(data);
 
 	return 0;
 }
@@ -461,7 +780,14 @@ static int davinci_mdio_runtime_resume(struct device *dev)
 {
 	struct davinci_mdio_data *data = dev_get_drvdata(dev);
 
-	davinci_mdio_enable(data);
+	if (data->manual_mode) {
+		davinci_mdio_disable(data);
+		davinci_mdio_enable_manual_mode(data);
+		davinci_mdio_sw_preamble(data);
+	} else {
+		davinci_mdio_enable(data);
+	}
+
 	return 0;
 }
 #endif
