diff --git a/drivers/net/ethernet/tehuti/Kconfig b/drivers/net/ethernet/tehuti/Kconfig
index 4198fd59e42e..71f22471f9a0 100644
--- a/drivers/net/ethernet/tehuti/Kconfig
+++ b/drivers/net/ethernet/tehuti/Kconfig
@@ -27,6 +27,7 @@ config TEHUTI_TN40
 	tristate "Tehuti Networks TN40xx 10G Ethernet adapters"
 	depends on PCI
 	select FW_LOADER
+	select AMCC_QT2025_PHY
 	help
 	  This driver supports 10G Ethernet adapters using Tehuti Networks
 	  TN40xx chips. Currently, adapters with Applied Micro Circuits
diff --git a/drivers/net/ethernet/tehuti/Makefile b/drivers/net/ethernet/tehuti/Makefile
index 1c468d99e476..7a0fe586a243 100644
--- a/drivers/net/ethernet/tehuti/Makefile
+++ b/drivers/net/ethernet/tehuti/Makefile
@@ -5,5 +5,5 @@
 
 obj-$(CONFIG_TEHUTI) += tehuti.o
 
-tn40xx-y := tn40.o
+tn40xx-y := tn40.o tn40_mdio.o
 obj-$(CONFIG_TEHUTI_TN40) += tn40xx.o
diff --git a/drivers/net/ethernet/tehuti/tn40.c b/drivers/net/ethernet/tehuti/tn40.c
index c8ed9b743753..2c50295f4e68 100644
--- a/drivers/net/ethernet/tehuti/tn40.c
+++ b/drivers/net/ethernet/tehuti/tn40.c
@@ -1285,18 +1285,26 @@ static void bdx_link_changed(struct bdx_priv *priv)
 		if (priv->link_loop_cnt++ > LINK_LOOP_MAX) {
 			/* MAC reset */
 			bdx_set_link_speed(priv, 0);
+			bdx_set_link_speed(priv, priv->phydev->speed);
 			priv->link_loop_cnt = 0;
 		}
 		write_reg(priv, 0x5150, 1000000);
 		return;
 	}
+
+	if (!netif_carrier_ok(priv->ndev)) {
+		netif_wake_queue(priv->ndev);
+		phy_print_status(priv->phydev);
+	}
 	priv->link = link;
+	netif_carrier_on(priv->ndev);
 }
 
 static inline void bdx_isr_extra(struct bdx_priv *priv, u32 isr)
 {
 	if (isr & (IR_LNKCHG0 | IR_LNKCHG1 | IR_TMR0)) {
 		netdev_dbg(priv->ndev, "isr = 0x%x\n", isr);
+		phy_mac_interrupt(priv->phydev);
 		bdx_link_changed(priv);
 	}
 }
@@ -1580,23 +1588,42 @@ static int bdx_close(struct net_device *ndev)
 
 	bdx_disable_interrupts(priv);
 	free_irq(priv->pdev->irq, priv->ndev);
+	phy_stop(priv->phydev);
+	phy_disconnect(priv->phydev);
 	bdx_sw_reset(priv);
 	destroy_rx_ring(priv);
 	destroy_tx_ring(priv);
 	return 0;
 }
 
+static void phy_handler(struct net_device *_dev)
+{
+}
+
 static int bdx_open(struct net_device *dev)
 {
 	struct bdx_priv *priv = netdev_priv(dev);
 	int ret;
 
 	bdx_sw_reset(priv);
+
+	ret = phy_connect_direct(priv->ndev, priv->phydev, phy_handler, PHY_INTERFACE_MODE_XAUI);
+	if (ret) {
+		netdev_err(dev, "failed to connect to phy %d\n", ret);
+		return ret;
+	}
+	phy_attached_info(priv->phydev);
+	phy_start(priv->phydev);
+
 	ret = bdx_start(priv);
 	if (ret) {
 		netdev_err(dev, "failed to start %d\n", ret);
+		phy_stop(priv->phydev);
+		phy_disconnect(priv->phydev);
 		return ret;
 	}
+	napi_enable(&priv->napi);
+	netif_start_queue(priv->ndev);
 	return 0;
 }
 
@@ -1872,6 +1899,11 @@ static int bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	priv->stats_flag = ((read_reg(priv, FPGA_VER) & 0xFFF) != 308);
 
+	ret = bdx_mdiobus_init(priv);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to find PHY.\n");
+		goto err_free_irq;
+	}
 	priv->isr_mask =
 	    IR_RX_FREE_0 | IR_LNKCHG0 | IR_PSE | IR_TMR0 | IR_RX_DESC_0 |
 	    IR_TX_FREE_0 | IR_TMR1;
diff --git a/drivers/net/ethernet/tehuti/tn40.h b/drivers/net/ethernet/tehuti/tn40.h
index fb43ebb5911f..06ab9a2cb42d 100644
--- a/drivers/net/ethernet/tehuti/tn40.h
+++ b/drivers/net/ethernet/tehuti/tn40.h
@@ -197,6 +197,9 @@ struct bdx_priv {
 	char *b0_va; /* Virtual address of buffer */
 
 	struct bdx_rx_page_table rx_page_table;
+
+	struct mii_bus *mdio;
+	struct phy_device *phydev;
 };
 
 /* RX FREE descriptor - 64bit */
@@ -283,4 +286,6 @@ static inline void write_reg(struct bdx_priv *priv, u32 reg, u32 val)
 	writel(val, priv->regs + reg);
 }
 
+int bdx_mdiobus_init(struct bdx_priv *priv);
+
 #endif /* _TN40XX_H */
diff --git a/drivers/net/ethernet/tehuti/tn40_mdio.c b/drivers/net/ethernet/tehuti/tn40_mdio.c
new file mode 100644
index 000000000000..f7f83c77e8b2
--- /dev/null
+++ b/drivers/net/ethernet/tehuti/tn40_mdio.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) Tehuti Networks Ltd. */
+
+#include "tn40.h"
+
+static u32 bdx_mdio_get(struct bdx_priv *priv)
+{
+	void __iomem *regs = priv->regs;
+
+#define BDX_MAX_MDIO_BUSY_LOOPS 1024
+	int tries = 0;
+
+	while (++tries < BDX_MAX_MDIO_BUSY_LOOPS) {
+		u32 mdio_cmd_stat = readl(regs + REG_MDIO_CMD_STAT);
+
+		if (GET_MDIO_BUSY(mdio_cmd_stat) == 0)
+			return mdio_cmd_stat;
+	}
+	dev_err(&priv->pdev->dev, "MDIO busy!\n");
+	return 0xFFFFFFFF;
+}
+
+static u16 bdx_mdio_read(struct bdx_priv *priv, int device, int port, u16 addr)
+{
+	void __iomem *regs = priv->regs;
+	u32 tmp_reg, i;
+	/* wait until MDIO is not busy */
+	if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+		return -1;
+
+	i = ((device & 0x1F) | ((port & 0x1F) << 5));
+	writel(i, regs + REG_MDIO_CMD);
+	writel((u32)addr, regs + REG_MDIO_ADDR);
+	tmp_reg = bdx_mdio_get(priv);
+	if (tmp_reg == 0xFFFFFFFF)
+		return -1;
+
+	writel(((1 << 15) | i), regs + REG_MDIO_CMD);
+	/* read CMD_STAT until not busy */
+	tmp_reg = bdx_mdio_get(priv);
+	if (tmp_reg == 0xFFFFFFFF)
+		return -1;
+
+	if (GET_MDIO_RD_ERR(tmp_reg)) {
+		dev_dbg(&priv->pdev->dev, "MDIO error after read command\n");
+		return -1;
+	}
+	tmp_reg = readl(regs + REG_MDIO_DATA);
+
+	return (tmp_reg & 0xFFFF);
+}
+
+static int bdx_mdio_write(struct bdx_priv *priv, int device, int port, u16 addr,
+			  u16 data)
+{
+	void __iomem *regs = priv->regs;
+	u32 tmp_reg;
+
+	/* wait until MDIO is not busy */
+	if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+		return -1;
+	writel(((device & 0x1F) | ((port & 0x1F) << 5)), regs + REG_MDIO_CMD);
+	writel((u32)addr, regs + REG_MDIO_ADDR);
+	if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+		return -1;
+	writel((u32)data, regs + REG_MDIO_DATA);
+	/* read CMD_STAT until not busy */
+	tmp_reg = bdx_mdio_get(priv);
+	if (tmp_reg == 0xFFFFFFFF)
+		return -1;
+
+	if (GET_MDIO_RD_ERR(tmp_reg)) {
+		dev_err(&priv->pdev->dev, "MDIO error after write command\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void bdx_mdio_set_speed(struct bdx_priv *priv, u32 speed)
+{
+	void __iomem *regs = priv->regs;
+	int mdio_cfg;
+
+	mdio_cfg = readl(regs + REG_MDIO_CMD_STAT);
+	if (speed == 1)
+		mdio_cfg = (0x7d << 7) | 0x08;	/* 1MHz */
+	else
+		mdio_cfg = 0xA08;	/* 6MHz */
+	mdio_cfg |= (1 << 6);
+	writel(mdio_cfg, regs + REG_MDIO_CMD_STAT);
+	msleep(100);
+}
+
+static int mdio_read_reg(struct mii_bus *mii_bus, int addr, int devnum, int regnum)
+{
+	return bdx_mdio_read(mii_bus->priv, devnum, addr, regnum);
+}
+
+static int mdio_write_reg(struct mii_bus *mii_bus, int addr, int devnum, int regnum, u16 val)
+{
+	return bdx_mdio_write(mii_bus->priv, devnum, addr, regnum, val);
+}
+
+int bdx_mdiobus_init(struct bdx_priv *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+	struct mii_bus *bus;
+	struct phy_device *phydev;
+	int ret;
+
+	bus = devm_mdiobus_alloc(&pdev->dev);
+	if (!bus)
+		return -ENOMEM;
+
+	bus->name = BDX_DRV_NAME;
+	bus->parent = &pdev->dev;
+	snprintf(bus->id, MII_BUS_ID_SIZE, "tn40xx-%x-%x",
+		 pci_domain_nr(pdev->bus), pci_dev_id(pdev));
+	bus->priv = priv;
+
+	bus->read_c45 = mdio_read_reg;
+	bus->write_c45 = mdio_write_reg;
+
+	ret = devm_mdiobus_register(&pdev->dev, bus);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register mdiobus %d %u %u\n",
+			ret, bus->state, MDIOBUS_UNREGISTERED);
+		return ret;
+	}
+
+	phydev = phy_find_first(bus);
+	if (!phydev) {
+		dev_err(&pdev->dev, "failed to find phy\n");
+		return -1;
+	}
+	phydev->irq = PHY_MAC_INTERRUPT;
+	priv->mdio = bus;
+	priv->phydev = phydev;
+	bdx_mdio_set_speed(priv, MDIO_SPEED_6MHZ);
+	return 0;
+}
