net: mediatek: split ethernet switch code from mtk_eth.c

mtk_eth.c contains not only the ethernet GMAC/DMA driver, but also
some ethernet switch initialization code. As we may add more switch
support in the future, it's better to move them out of mtk_eth.c to
avoid increasing the code complexity.

Since not all switches are supported for a particular board, Kconfig
options are added to allow user to select which switch should be
built into u-boot. If multiple switches are selected, auto-detecting
can also be enabled.

diff --git a/drivers/net/mtk_eth/mt753x.c b/drivers/net/mtk_eth/mt753x.c
new file mode 100644
index 0000000..cdd52f3
--- /dev/null
+++ b/drivers/net/mtk_eth/mt753x.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 MediaTek Inc.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ * Author: Mark Lee <mark-mc.lee@mediatek.com>
+ */
+
+#include <errno.h>
+#include <time.h>
+#include "mtk_eth.h"
+#include "mt753x.h"
+
+/*
+ * MT753x Internal Register Address Bits
+ * -------------------------------------------------------------------
+ * | 15  14  13  12  11  10   9   8   7   6 | 5   4   3   2 | 1   0  |
+ * |----------------------------------------|---------------|--------|
+ * |              Page Address              |  Reg Address  | Unused |
+ * -------------------------------------------------------------------
+ */
+
+int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg,
+			   u32 *data)
+{
+	int ret, low_word, high_word;
+
+	/* Write page address */
+	ret = mtk_mii_write(priv, smi_addr, 0x1f, reg >> 6);
+	if (ret)
+		return ret;
+
+	/* Read low word */
+	low_word = mtk_mii_read(priv, smi_addr, (reg >> 2) & 0xf);
+	if (low_word < 0)
+		return low_word;
+
+	/* Read high word */
+	high_word = mtk_mii_read(priv, smi_addr, 0x10);
+	if (high_word < 0)
+		return high_word;
+
+	if (data)
+		*data = ((u32)high_word << 16) | (low_word & 0xffff);
+
+	return 0;
+}
+
+int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
+{
+	return __mt753x_mdio_reg_read(priv->epriv.eth, priv->smi_addr, reg,
+				      data);
+}
+
+int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
+{
+	int ret;
+
+	/* Write page address */
+	ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x1f, reg >> 6);
+	if (ret)
+		return ret;
+
+	/* Write low word */
+	ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, (reg >> 2) & 0xf,
+			    data & 0xffff);
+	if (ret)
+		return ret;
+
+	/* Write high word */
+	return mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x10, data >> 16);
+}
+
+int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
+{
+	return priv->reg_read(priv, reg, data);
+}
+
+int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
+{
+	return priv->reg_write(priv, reg, data);
+}
+
+void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set)
+{
+	u32 val;
+
+	priv->reg_read(priv, reg, &val);
+	val &= ~clr;
+	val |= set;
+	priv->reg_write(priv, reg, val);
+}
+
+/* Indirect MDIO clause 22/45 access */
+static int mt7531_mii_rw(struct mt753x_switch_priv *priv, int phy, int reg,
+			 u16 data, u32 cmd, u32 st)
+{
+	u32 val, timeout_ms;
+	ulong timeout;
+	int ret = 0;
+
+	val = (st << MDIO_ST_S) |
+	      ((cmd << MDIO_CMD_S) & MDIO_CMD_M) |
+	      ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) |
+	      ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M);
+
+	if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR)
+		val |= data & MDIO_RW_DATA_M;
+
+	mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST);
+
+	timeout_ms = 100;
+	timeout = get_timer(0);
+	while (1) {
+		mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
+
+		if ((val & PHY_ACS_ST) == 0)
+			break;
+
+		if (get_timer(timeout) > timeout_ms)
+			return -ETIMEDOUT;
+	}
+
+	if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) {
+		mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
+		ret = val & MDIO_RW_DATA_M;
+	}
+
+	return ret;
+}
+
+int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg)
+{
+	u8 phy_addr;
+
+	if (phy >= MT753X_NUM_PHYS)
+		return -EINVAL;
+
+	phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy);
+
+	return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ,
+			     MDIO_ST_C22);
+}
+
+int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val)
+{
+	u8 phy_addr;
+
+	if (phy >= MT753X_NUM_PHYS)
+		return -EINVAL;
+
+	phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy);
+
+	return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE,
+			     MDIO_ST_C22);
+}
+
+int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
+		    u16 reg)
+{
+	u8 phy_addr;
+	int ret;
+
+	if (addr >= MT753X_NUM_PHYS)
+		return -EINVAL;
+
+	phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr);
+
+	ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
+			    MDIO_ST_C45);
+	if (ret)
+		return ret;
+
+	return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45,
+			     MDIO_ST_C45);
+}
+
+int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
+		     u16 reg, u16 val)
+{
+	u8 phy_addr;
+	int ret;
+
+	if (addr >= MT753X_NUM_PHYS)
+		return 0;
+
+	phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr);
+
+	ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
+			    MDIO_ST_C45);
+	if (ret)
+		return ret;
+
+	return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE,
+			     MDIO_ST_C45);
+}
+
+static int mt7531_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+	struct mt753x_switch_priv *priv = bus->priv;
+
+	if (devad < 0)
+		return mt7531_mii_read(priv, addr, reg);
+
+	return mt7531_mmd_read(priv, addr, devad, reg);
+}
+
+static int mt7531_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+			     u16 val)
+{
+	struct mt753x_switch_priv *priv = bus->priv;
+
+	if (devad < 0)
+		return mt7531_mii_write(priv, addr, reg, val);
+
+	return mt7531_mmd_write(priv, addr, devad, reg, val);
+}
+
+int mt7531_mdio_register(struct mt753x_switch_priv *priv)
+{
+	struct mii_dev *mdio_bus = mdio_alloc();
+	int ret;
+
+	if (!mdio_bus)
+		return -ENOMEM;
+
+	mdio_bus->read = mt7531_mdio_read;
+	mdio_bus->write = mt7531_mdio_write;
+	snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name);
+
+	mdio_bus->priv = priv;
+
+	ret = mdio_register(mdio_bus);
+	if (ret) {
+		mdio_free(mdio_bus);
+		return ret;
+	}
+
+	priv->mdio_bus = mdio_bus;
+
+	return 0;
+}
+
+void mt753x_port_isolation(struct mt753x_switch_priv *priv)
+{
+	u32 i;
+
+	for (i = 0; i < MT753X_NUM_PORTS; i++) {
+		/* Set port matrix mode */
+		if (i != 6)
+			mt753x_reg_write(priv, PCR_REG(i),
+					 (0x40 << PORT_MATRIX_S));
+		else
+			mt753x_reg_write(priv, PCR_REG(i),
+					 (0x3f << PORT_MATRIX_S));
+
+		/* Set port mode to user port */
+		mt753x_reg_write(priv, PVC_REG(i),
+				 (0x8100 << STAG_VPID_S) |
+				 (VLAN_ATTR_USER << VLAN_ATTR_S));
+	}
+}