From: Christian Marangi <ansuelsmth@gmail.com>
To: Tom Rini <trini@konsulko.com>,
Joe Hershberger <joe.hershberger@ni.com>,
Ramon Fried <rfried.dev@gmail.com>,
Weijie Gao <weijie.gao@mediatek.com>,
Christian Marangi <ansuelsmth@gmail.com>,
u-boot@lists.denx.de
Subject: [PATCH v2 4/5] net: mediatek: move MT7531 MMIO MDIO to dedicated driver
Date: Sat, 20 Sep 2025 18:09:44 +0200 [thread overview]
Message-ID: <20250920161013.31799-5-ansuelsmth@gmail.com> (raw)
In-Reply-To: <20250920161013.31799-1-ansuelsmth@gmail.com>
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@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 | 85 ++++++++++++++---
6 files changed, 254 insertions(+), 13 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 b77660be55c..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@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,25 +33,34 @@ 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;
for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ u16 addr = MT753X_PHY_ADDR(priv->phy_base, i);
+
/* Set PHY to PHY page 1 */
- mt7531_mii_write(priv, i, 0x1f, 0x1);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ 0x1f, 0x1);
/* Enable HW auto downshift */
- val = mt7531_mii_read(priv, i, 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, i, 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, i, 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, i, 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, i, 0x1f, 0x0);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ 0x1f, 0x0);
}
}
@@ -63,23 +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;
- u16 phy_val;
+ 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_val = mt7531_mii_read(priv, i, MII_BMCR);
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+ MDIO_DEVAD_NONE, MII_BMCR);
phy_val |= BMCR_PDOWN;
- mt7531_mii_write(priv, i, MII_BMCR, phy_val);
+ mt7531_mdio_mmio_write(mdio_bus, phy_addr, MDIO_DEVAD_NONE,
+ MII_BMCR, phy_val);
}
switch (priv->epriv.phy_interface) {
@@ -132,14 +187,17 @@ static int mt7988_setup(struct mtk_eth_switch_priv *swpriv)
/* Turn on PHYs */
for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_val = mt7531_mii_read(priv, i, MII_BMCR);
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+ MDIO_DEVAD_NONE, MII_BMCR);
phy_val &= ~BMCR_PDOWN;
- mt7531_mii_write(priv, i, 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)
@@ -148,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
next prev parent reply other threads:[~2025-09-20 16:10 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-20 16:09 [PATCH v2 0/5] net: mediatek: mt7988: various fixup + MDIO detach Christian Marangi
2025-09-20 16:09 ` [PATCH v2 1/5] net: mediatek: mt7531/7988: fix broken PHY turn ON/OFF Christian Marangi
2025-09-20 16:09 ` [PATCH v2 2/5] net: mediatek: mt7988: restore PHY page on PHY setting exit Christian Marangi
2025-09-20 16:09 ` [PATCH v2 3/5] net: mediatek: mt7988: free allocated MDIO bus on cleanup Christian Marangi
2025-09-20 16:09 ` Christian Marangi [this message]
2025-09-20 16:09 ` [PATCH v2 5/5] net: airoha: bind MDIO controller on Ethernet load Christian Marangi
2025-10-22 13:42 ` Jerome Forissier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250920161013.31799-5-ansuelsmth@gmail.com \
--to=ansuelsmth@gmail.com \
--cc=joe.hershberger@ni.com \
--cc=rfried.dev@gmail.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
--cc=weijie.gao@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox