[PATCH 4/5] net: mediatek: move MT7531 MMIO MDIO to dedicated driver

Christian Marangi ansuelsmth at gmail.com
Sat Sep 20 16:36:49 CEST 2025


In preparation for support of MDIO on AN7581, move the MT7531 MMIO logic
to a dedicated driver and permit usage of the mdio read/write function
to the mtk_eth driver.

This only affect MT7988 that can use MMIO operation to access the Switch
register. The MT7988 code is updated to make use of the external driver.

This permits Airoha driver to make use of DM_MDIO to bind for the MT7531
driver that have the same exact register.

Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 drivers/net/Kconfig            |   3 +
 drivers/net/Makefile           |   1 +
 drivers/net/mdio-mt7531-mmio.c | 168 +++++++++++++++++++++++++++++++++
 drivers/net/mdio-mt7531-mmio.h |   9 ++
 drivers/net/mtk_eth/Kconfig    |   1 +
 drivers/net/mtk_eth/mt7988.c   |  79 +++++++++++++---
 6 files changed, 249 insertions(+), 12 deletions(-)
 create mode 100644 drivers/net/mdio-mt7531-mmio.c
 create mode 100644 drivers/net/mdio-mt7531-mmio.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d1cb69f85ad..cee4a3fc9bd 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -966,6 +966,9 @@ config TSEC_ENET
 	  This driver implements support for the (Enhanced) Three-Speed
 	  Ethernet Controller found on Freescale SoCs.
 
+config MDIO_MT7531_MMIO
+	bool
+
 source "drivers/net/mtk_eth/Kconfig"
 
 config HIFEMAC_ETH
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index f8f9a71f815..a3c3420898c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
 obj-$(CONFIG_MDIO_GPIO_BITBANG) += mdio_gpio.o
+obj-$(CONFIG_MDIO_MT7531_MMIO) += mdio-mt7531-mmio.o
 obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
 obj-$(CONFIG_MDIO_MUX_MESON_G12A) += mdio_mux_meson_g12a.o
 obj-$(CONFIG_MDIO_MUX_MESON_GXL) += mdio_mux_meson_gxl.o
diff --git a/drivers/net/mdio-mt7531-mmio.c b/drivers/net/mdio-mt7531-mmio.c
new file mode 100644
index 00000000000..3e325ca58da
--- /dev/null
+++ b/drivers/net/mdio-mt7531-mmio.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <asm/io.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <miiphy.h>
+
+#define MT7531_PHY_IAC			0x701c
+#define   MT7531_PHY_ACS_ST		BIT(31)
+#define   MT7531_MDIO_REG_ADDR_CL22	GENMASK(29, 25)
+#define   MT7531_MDIO_DEV_ADDR		MT7531_MDIO_REG_ADDR_CL22
+#define   MT7531_MDIO_PHY_ADDR		GENMASK(24, 20)
+#define   MT7531_MDIO_CMD		GENMASK(19, 18)
+#define   MT7531_MDIO_CMD_READ_CL45	FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x3)
+#define   MT7531_MDIO_CMD_READ_CL22	FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x2)
+#define   MT7531_MDIO_CMD_WRITE		FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x1)
+#define   MT7531_MDIO_CMD_ADDR		FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x0)
+#define   MT7531_MDIO_ST		GENMASK(17, 16)
+#define   MT7531_MDIO_ST_CL22		FIELD_PREP_CONST(MT7531_MDIO_ST, 0x1)
+#define   MT7531_MDIO_ST_CL45		FIELD_PREP_CONST(MT7531_MDIO_ST, 0x0)
+#define   MT7531_MDIO_RW_DATA		GENMASK(15, 0)
+#define   MT7531_MDIO_REG_ADDR_CL45	MT7531_MDIO_RW_DATA
+
+#define MT7531_MDIO_TIMEOUT		100000
+#define MT7531_MDIO_SLEEP		20
+
+struct mt7531_mdio_priv {
+	phys_addr_t switch_regs;
+};
+
+static int mt7531_mdio_wait_busy(struct mt7531_mdio_priv *priv)
+{
+	unsigned int busy;
+
+	return readl_poll_sleep_timeout(priv->switch_regs + MT7531_PHY_IAC,
+					busy, (busy & MT7531_PHY_ACS_ST) == 0,
+					MT7531_MDIO_SLEEP, MT7531_MDIO_TIMEOUT);
+}
+
+static int mt7531_mdio_read(struct mt7531_mdio_priv *priv, int addr, int devad, int reg)
+{
+	u32 val;
+
+	if (devad != MDIO_DEVAD_NONE) {
+		if (mt7531_mdio_wait_busy(priv))
+			return -ETIMEDOUT;
+
+		val = MT7531_PHY_ACS_ST |
+		      MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
+		      FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+		      FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
+		      FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);
+
+		writel(val, priv->switch_regs + MT7531_PHY_IAC);
+	}
+
+	if (mt7531_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr);
+	if (devad != MDIO_DEVAD_NONE)
+		val |= MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_READ_CL45 |
+		       FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
+	else
+		val |= MT7531_MDIO_ST_CL22 | MT7531_MDIO_CMD_READ_CL22 |
+		       FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);
+
+	writel(val, priv->switch_regs + MT7531_PHY_IAC);
+
+	if (mt7531_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	val = readl(priv->switch_regs + MT7531_PHY_IAC);
+	return val & MT7531_MDIO_RW_DATA;
+}
+
+static int mt7531_mdio_write(struct mt7531_mdio_priv *priv, int addr, int devad,
+			     int reg, u16 value)
+{
+	u32 val;
+
+	if (devad != MDIO_DEVAD_NONE) {
+		if (mt7531_mdio_wait_busy(priv))
+			return -ETIMEDOUT;
+
+		val = MT7531_PHY_ACS_ST |
+		      MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
+		      FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+		      FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
+		      FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);
+
+		writel(val, priv->switch_regs + MT7531_PHY_IAC);
+	}
+
+	if (mt7531_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+	      MT7531_MDIO_CMD_WRITE | FIELD_PREP(MT7531_MDIO_RW_DATA, value);
+	if (devad != MDIO_DEVAD_NONE)
+		val |= MT7531_MDIO_ST_CL45 |
+		       FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
+	else
+		val |= MT7531_MDIO_ST_CL22 |
+		       FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);
+
+	writel(val, priv->switch_regs + MT7531_PHY_IAC);
+
+	if (mt7531_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+int mt7531_mdio_mmio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+	struct mt7531_mdio_priv *priv = bus->priv;
+
+	return mt7531_mdio_read(priv, addr, devad, reg);
+}
+
+int mt7531_mdio_mmio_write(struct mii_dev *bus, int addr, int devad,
+			   int reg, u16 value)
+{
+	struct mt7531_mdio_priv *priv = bus->priv;
+
+	return mt7531_mdio_write(priv, addr, devad, reg, value);
+}
+
+static int dm_mt7531_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+	struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+	return mt7531_mdio_read(priv, addr, devad, reg);
+}
+
+static int dm_mt7531_mdio_write(struct udevice *dev, int addr, int devad,
+				int reg, u16 value)
+{
+	struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+	return mt7531_mdio_write(priv, addr, devad, reg, value);
+}
+
+static const struct mdio_ops mt7531_mdio_ops = {
+	.read = dm_mt7531_mdio_read,
+	.write = dm_mt7531_mdio_write,
+};
+
+static int mt7531_mdio_probe(struct udevice *dev)
+{
+	struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+	priv->switch_regs = dev_read_addr(dev);
+	if (priv->switch_regs == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(mt7531_mdio) = {
+	.name           = "mt7531-mdio-mmio",
+	.id             = UCLASS_MDIO,
+	.probe          = mt7531_mdio_probe,
+	.ops            = &mt7531_mdio_ops,
+	.priv_auto	  = sizeof(struct mt7531_mdio_priv),
+};
diff --git a/drivers/net/mdio-mt7531-mmio.h b/drivers/net/mdio-mt7531-mmio.h
new file mode 100644
index 00000000000..f98102cb939
--- /dev/null
+++ b/drivers/net/mdio-mt7531-mmio.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+struct mt7531_mdio_mmio_priv {
+	phys_addr_t switch_regs;
+};
+
+int mt7531_mdio_mmio_read(struct mii_dev *bus, int addr, int devad, int reg);
+int mt7531_mdio_mmio_write(struct mii_dev *bus, int addr, int devad,
+			   int reg, u16 value);
diff --git a/drivers/net/mtk_eth/Kconfig b/drivers/net/mtk_eth/Kconfig
index e8cdf408237..8716eb86064 100644
--- a/drivers/net/mtk_eth/Kconfig
+++ b/drivers/net/mtk_eth/Kconfig
@@ -30,6 +30,7 @@ config MTK_ETH_SWITCH_MT7531
 config MTK_ETH_SWITCH_MT7988
 	bool "Support for MediaTek MT7988 built-in ethernet switch"
 	depends on TARGET_MT7988
+	select MDIO_MT7531_MMIO
 	default y
 
 config MTK_ETH_SWITCH_AN8855
diff --git a/drivers/net/mtk_eth/mt7988.c b/drivers/net/mtk_eth/mt7988.c
index 797070add7a..29b6363cbd7 100644
--- a/drivers/net/mtk_eth/mt7988.c
+++ b/drivers/net/mtk_eth/mt7988.c
@@ -6,6 +6,7 @@
  * Author: Mark Lee <mark-mc.lee at mediatek.com>
  */
 
+#include <malloc.h>
 #include <miiphy.h>
 #include <linux/delay.h>
 #include <linux/mdio.h>
@@ -14,6 +15,8 @@
 #include "mtk_eth.h"
 #include "mt753x.h"
 
+#include "../mdio-mt7531-mmio.h"
+
 static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
 {
 	*data = readl(priv->epriv.ethsys_base + GSW_BASE + reg);
@@ -30,6 +33,7 @@ static int mt7988_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
 
 static void mt7988_phy_setting(struct mt753x_switch_priv *priv)
 {
+	struct mii_dev *mdio_bus = priv->mdio_bus;
 	u16 val;
 	u32 i;
 
@@ -37,20 +41,26 @@ static void mt7988_phy_setting(struct mt753x_switch_priv *priv)
 		u16 addr = MT753X_PHY_ADDR(priv->phy_base, i);
 
 		/* Set PHY to PHY page 1 */
-		mt7531_mii_write(priv, addr, 0x1f, 0x1);
+		mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+				       0x1f, 0x1);
 
 		/* Enable HW auto downshift */
-		val = mt7531_mii_read(priv, addr, PHY_EXT_REG_14);
+		val = mt7531_mdio_mmio_read(mdio_bus, addr, MDIO_DEVAD_NONE,
+					    PHY_EXT_REG_14);
 		val |= PHY_EN_DOWN_SHFIT;
-		mt7531_mii_write(priv, addr, PHY_EXT_REG_14, val);
+		mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+				       PHY_EXT_REG_14, val);
 
 		/* PHY link down power saving enable */
-		val = mt7531_mii_read(priv, addr, PHY_EXT_REG_17);
+		val = mt7531_mdio_mmio_read(mdio_bus, addr, MDIO_DEVAD_NONE,
+					    PHY_EXT_REG_17);
 		val |= PHY_LINKDOWN_POWER_SAVING_EN;
-		mt7531_mii_write(priv, addr, PHY_EXT_REG_17, val);
+		mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+				       PHY_EXT_REG_17, val);
 
 		/* Restore PHY to PHY page 0 */
-		mt7531_mii_write(priv, addr, 0x1f, 0x0);
+		mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+				       0x1f, 0x0);
 	}
 }
 
@@ -65,24 +75,66 @@ static void mt7988_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
 	mt7988_reg_write(priv, PMCR_REG(6), pmcr);
 }
 
+static int mt7988_mdio_register(struct mt753x_switch_priv *priv)
+{
+	struct mt7531_mdio_mmio_priv *mdio_priv;
+	struct mii_dev *mdio_bus = mdio_alloc();
+	int ret;
+
+	if (!mdio_bus)
+		return -ENOMEM;
+
+	mdio_priv = malloc(sizeof(*mdio_priv));
+	if (!mdio_priv)
+		return -ENOMEM;
+
+	mdio_priv->switch_regs = (phys_addr_t)priv->epriv.ethsys_base + GSW_BASE;
+
+	mdio_bus->read = mt7531_mdio_mmio_read;
+	mdio_bus->write = mt7531_mdio_mmio_write;
+	snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name);
+
+	mdio_bus->priv = mdio_priv;
+
+	ret = mdio_register(mdio_bus);
+	if (ret) {
+		free(mdio_bus->priv);
+		mdio_free(mdio_bus);
+		return ret;
+	}
+
+	priv->mdio_bus = mdio_bus;
+
+	return 0;
+}
+
 static int mt7988_setup(struct mtk_eth_switch_priv *swpriv)
 {
 	struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+	struct mii_dev *mdio_bus;
 	u16 phy_addr, phy_val;
+	int ret, i;
 	u32 pmcr;
-	int i;
 
 	priv->smi_addr = MT753X_DFL_SMI_ADDR;
 	priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK;
 	priv->reg_read = mt7988_reg_read;
 	priv->reg_write = mt7988_reg_write;
 
+	ret = mt7988_mdio_register(priv);
+	if (ret)
+		return ret;
+
+	mdio_bus = priv->mdio_bus;
+
 	/* Turn off PHYs */
 	for (i = 0; i < MT753X_NUM_PHYS; i++) {
 		phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
-		phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR);
+		phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+						MDIO_DEVAD_NONE, MII_BMCR);
 		phy_val |= BMCR_PDOWN;
-		mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+		mt7531_mdio_mmio_write(mdio_bus, phy_addr, MDIO_DEVAD_NONE,
+				       MII_BMCR, phy_val);
 	}
 
 	switch (priv->epriv.phy_interface) {
@@ -136,14 +188,16 @@ static int mt7988_setup(struct mtk_eth_switch_priv *swpriv)
 	/* Turn on PHYs */
 	for (i = 0; i < MT753X_NUM_PHYS; i++) {
 		phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
-		phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR);
+		phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+						MDIO_DEVAD_NONE, MII_BMCR);
 		phy_val &= ~BMCR_PDOWN;
-		mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+		mt7531_mdio_mmio_write(mdio_bus, phy_addr, MDIO_DEVAD_NONE,
+				       MII_BMCR, phy_val);
 	}
 
 	mt7988_phy_setting(priv);
 
-	return mt7531_mdio_register(priv);
+	return 0;
 }
 
 static int mt7988_cleanup(struct mtk_eth_switch_priv *swpriv)
@@ -152,6 +206,7 @@ static int mt7988_cleanup(struct mtk_eth_switch_priv *swpriv)
 	struct mii_dev *mdio_bus = priv->mdio_bus;
 
 	mdio_unregister(mdio_bus);
+	free(mdio_bus->priv);
 	mdio_free(mdio_bus);
 
 	return 0;
-- 
2.51.0



More information about the U-Boot mailing list