summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig35
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/designware.c125
-rw-r--r--drivers/net/designware.h6
-rw-r--r--drivers/net/dwc_eth_xgmac.c82
-rw-r--r--drivers/net/dwc_eth_xgmac_socfpga.c27
-rw-r--r--drivers/net/dwmac_socfpga.c3
-rw-r--r--drivers/net/fsl_enetc.c508
-rw-r--r--drivers/net/fsl_enetc.h88
-rw-r--r--drivers/net/fsl_enetc_mdio.c20
-rw-r--r--drivers/net/fsl_enetc_netc_blk_ctrl.c346
-rw-r--r--drivers/net/mtk_eth/Kconfig39
-rw-r--r--drivers/net/mtk_eth/Makefile10
-rw-r--r--drivers/net/mtk_eth/an8855.c1095
-rw-r--r--drivers/net/mtk_eth/mt7530.c281
-rw-r--r--drivers/net/mtk_eth/mt7531.c293
-rw-r--r--drivers/net/mtk_eth/mt753x.c262
-rw-r--r--drivers/net/mtk_eth/mt753x.h286
-rw-r--r--drivers/net/mtk_eth/mt7988.c160
-rw-r--r--drivers/net/mtk_eth/mtk_eth.c (renamed from drivers/net/mtk_eth.c)981
-rw-r--r--drivers/net/mtk_eth/mtk_eth.h (renamed from drivers/net/mtk_eth.h)301
-rw-r--r--drivers/net/phy/Kconfig4
-rw-r--r--drivers/net/phy/miiphybb.c93
-rw-r--r--drivers/net/phy/ti_phy_init.c48
-rw-r--r--drivers/net/ravb.c14
25 files changed, 3765 insertions, 1345 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 88ff025a37b..1563404ca17 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -966,26 +966,7 @@ config TSEC_ENET
This driver implements support for the (Enhanced) Three-Speed
Ethernet Controller found on Freescale SoCs.
-config MEDIATEK_ETH
- bool "MediaTek Ethernet GMAC Driver"
- select PHYLIB
- select DM_GPIO
- select DM_RESET
- help
- This Driver support MediaTek Ethernet GMAC
- Say Y to enable support for the MediaTek Ethernet GMAC.
-
-if MEDIATEK_ETH
-
-config MTK_ETH_SGMII
- bool
- default y if ARCH_MEDIATEK && !TARGET_MT7623
-
-config MTK_ETH_XGMII
- bool
- default y if TARGET_MT7987 || TARGET_MT7988
-
-endif # MEDIATEK_ETH
+source "drivers/net/mtk_eth/Kconfig"
config HIFEMAC_ETH
bool "HiSilicon Fast Ethernet Controller"
@@ -1019,6 +1000,20 @@ config FSL_ENETC
This driver supports the NXP ENETC Ethernet controller found on some
of the NXP SoCs.
+config FSL_ENETC_NETC_BLK_CTRL
+ bool "NXP ENETC NETC blocks control driver"
+ depends on FSL_ENETC && IMX95
+ default y if IMX95
+ help
+ This driver configures Integrated Endpoint Register Block (IERB) and
+ Privileged Register Block (PRB) of NETC. For i.MX platforms, it also
+ includes the configuration of NETCMIX block.
+ The IERB contains registers that are used for pre-boot initialization,
+ debug, and non-customer configuration. The PRB controls global reset
+ and global error handling for NETC. The NETCMIX block is mainly used
+ to set MII protocol and PCS protocol of the links, it also contains
+ settings for some other functions.
+
config MDIO_GPIO_BITBANG
bool "GPIO bitbanging MDIO driver"
depends on DM_MDIO && DM_GPIO
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e51a917933e..80d70212971 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_FEC_MXC) += fec_mxc.o
obj-$(CONFIG_FMAN_ENET) += fm/
obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o
obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
+obj-$(CONFIG_FSL_ENETC_NETC_BLK_CTRL) += fsl_enetc_netc_blk_ctrl.o
obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o
obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
@@ -68,7 +69,7 @@ obj-$(CONFIG_MDIO_MUX_MESON_GXL) += mdio_mux_meson_gxl.o
obj-$(CONFIG_MDIO_MUX_MMIOREG) += mdio_mux_mmioreg.o
obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o
obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o
-obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o
+obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth/
obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o
obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 07b0f49ef58..0a1fff38727 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -741,6 +741,18 @@ int designware_eth_probe(struct udevice *dev)
puts("Error enabling phy supply\n");
return ret;
}
+#if IS_ENABLED(CONFIG_ARCH_NPCM8XX)
+ int phy_uv;
+
+ phy_uv = dev_read_u32_default(dev, "phy-supply-microvolt", 0);
+ if (phy_uv) {
+ ret = regulator_set_value(phy_supply, phy_uv);
+ if (ret) {
+ puts("Error setting phy voltage\n");
+ return ret;
+ }
+ }
+#endif
}
#endif
@@ -784,6 +796,39 @@ int designware_eth_probe(struct udevice *dev)
priv->bus = miiphy_get_dev_by_name(dev->name);
priv->dev = dev;
+#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
+ if (dev_read_bool(dev, "snps,bitbang-mii")) {
+ int bus_idx;
+
+ debug("\n%s: use bitbang mii..\n", dev->name);
+ ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0,
+ &priv->mdc_gpio, GPIOD_IS_OUT
+ | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ debug("no mdc-gpio\n");
+ return ret;
+ }
+ ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0,
+ &priv->mdio_gpio, GPIOD_IS_OUT
+ | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ debug("no mdio-gpio\n");
+ return ret;
+ }
+ priv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1);
+
+ for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; bus_idx++) {
+ if (!bb_miiphy_buses[bus_idx].priv) {
+ bb_miiphy_buses[bus_idx].priv = priv;
+ strlcpy(bb_miiphy_buses[bus_idx].name, priv->bus->name,
+ MDIO_NAME_LEN);
+ priv->bus->read = bb_miiphy_read;
+ priv->bus->write = bb_miiphy_write;
+ break;
+ }
+ }
+ }
+#endif
ret = dw_phy_init(priv, dev);
debug("%s, ret=%d\n", __func__, ret);
if (!ret)
@@ -894,3 +939,83 @@ static struct pci_device_id supported[] = {
};
U_BOOT_PCI_DEVICE(eth_designware, supported);
+
+#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
+static int dw_eth_bb_mdio_active(struct bb_miiphy_bus *bus)
+{
+ struct dw_eth_dev *priv = bus->priv;
+ struct gpio_desc *desc = &priv->mdio_gpio;
+
+ desc->flags = 0;
+ dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+
+ return 0;
+}
+
+static int dw_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus)
+{
+ struct dw_eth_dev *priv = bus->priv;
+ struct gpio_desc *desc = &priv->mdio_gpio;
+
+ desc->flags = 0;
+ dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN);
+
+ return 0;
+}
+
+static int dw_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
+{
+ struct dw_eth_dev *priv = bus->priv;
+
+ if (v)
+ dm_gpio_set_value(&priv->mdio_gpio, 1);
+ else
+ dm_gpio_set_value(&priv->mdio_gpio, 0);
+
+ return 0;
+}
+
+static int dw_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
+{
+ struct dw_eth_dev *priv = bus->priv;
+
+ *v = dm_gpio_get_value(&priv->mdio_gpio);
+
+ return 0;
+}
+
+static int dw_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
+{
+ struct dw_eth_dev *priv = bus->priv;
+
+ if (v)
+ dm_gpio_set_value(&priv->mdc_gpio, 1);
+ else
+ dm_gpio_set_value(&priv->mdc_gpio, 0);
+
+ return 0;
+}
+
+static int dw_eth_bb_delay(struct bb_miiphy_bus *bus)
+{
+ struct dw_eth_dev *priv = bus->priv;
+
+ udelay(priv->bb_delay);
+ return 0;
+}
+
+struct bb_miiphy_bus bb_miiphy_buses[] = {
+ {
+ .name = BB_MII_DEVNAME,
+ .mdio_active = dw_eth_bb_mdio_active,
+ .mdio_tristate = dw_eth_bb_mdio_tristate,
+ .set_mdio = dw_eth_bb_set_mdio,
+ .get_mdio = dw_eth_bb_get_mdio,
+ .set_mdc = dw_eth_bb_set_mdc,
+ .delay = dw_eth_bb_delay,
+ .priv = NULL,
+ }
+};
+
+int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
+#endif
diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..cccf9d54e02 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -229,7 +229,11 @@ struct dw_eth_dev {
u32 max_speed;
u32 tx_currdescnum;
u32 rx_currdescnum;
-
+#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
+ u32 bb_delay;
+ struct gpio_desc mdc_gpio;
+ struct gpio_desc mdio_gpio;
+#endif
struct eth_mac_regs *mac_regs_p;
struct eth_dma_regs *dma_regs_p;
#if CONFIG_IS_ENABLED(DM_GPIO)
diff --git a/drivers/net/dwc_eth_xgmac.c b/drivers/net/dwc_eth_xgmac.c
index d3e5f9255f5..cf8227b1b4d 100644
--- a/drivers/net/dwc_eth_xgmac.c
+++ b/drivers/net/dwc_eth_xgmac.c
@@ -152,7 +152,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO not idle at entry: %d\n", ret);
+ pr_err("%s MDIO not idle at entry: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -172,7 +174,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO not idle at entry: %d\n", ret);
+ pr_err("%s MDIO not idle at entry: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -181,7 +185,9 @@ static int xgmac_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO read didn't complete: %d\n", ret);
+ pr_err("%s MDIO read didn't complete: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -206,7 +212,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO not idle at entry: %d\n", ret);
+ pr_err("%s MDIO not idle at entry: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -229,7 +237,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO not idle at entry: %d\n", ret);
+ pr_err("%s MDIO not idle at entry: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -238,7 +248,9 @@ static int xgmac_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
ret = xgmac_mdio_wait_idle(xgmac);
if (ret) {
- pr_err("MDIO write didn't complete: %d\n", ret);
+ pr_err("%s MDIO write didn't complete: %d\n",
+ xgmac->dev->name, ret);
+
return ret;
}
@@ -323,7 +335,7 @@ static int xgmac_adjust_link(struct udevice *dev)
else
ret = xgmac_set_half_duplex(dev);
if (ret < 0) {
- pr_err("xgmac_set_*_duplex() failed: %d\n", ret);
+ pr_err("%s xgmac_set_*_duplex() failed: %d\n", dev->name, ret);
return ret;
}
@@ -341,26 +353,28 @@ static int xgmac_adjust_link(struct udevice *dev)
ret = xgmac_set_mii_speed_10(dev);
break;
default:
- pr_err("invalid speed %d\n", xgmac->phy->speed);
+ pr_err("%s invalid speed %d\n", dev->name, xgmac->phy->speed);
return -EINVAL;
}
if (ret < 0) {
- pr_err("xgmac_set_*mii_speed*() failed: %d\n", ret);
+ pr_err("%s xgmac_set_*mii_speed*() failed: %d\n", dev->name, ret);
return ret;
}
if (en_calibration) {
ret = xgmac->config->ops->xgmac_calibrate_pads(dev);
if (ret < 0) {
- pr_err("xgmac_calibrate_pads() failed: %d\n",
- ret);
+ pr_err("%s xgmac_calibrate_pads() failed: %d\n",
+ dev->name, ret);
+
return ret;
}
} else {
ret = xgmac->config->ops->xgmac_disable_calibration(dev);
if (ret < 0) {
- pr_err("xgmac_disable_calibration() failed: %d\n",
- ret);
+ pr_err("%s xgmac_disable_calibration() failed: %d\n",
+ dev->name, ret);
+
return ret;
}
}
@@ -456,7 +470,7 @@ static int xgmac_start(struct udevice *dev)
ret = xgmac->config->ops->xgmac_start_resets(dev);
if (ret < 0) {
- pr_err("xgmac_start_resets() failed: %d\n", ret);
+ pr_err("%s xgmac_start_resets() failed: %d\n", dev->name, ret);
goto err;
}
@@ -466,13 +480,13 @@ static int xgmac_start(struct udevice *dev)
XGMAC_DMA_MODE_SWR, false,
xgmac->config->swr_wait, false);
if (ret) {
- pr_err("XGMAC_DMA_MODE_SWR stuck: %d\n", ret);
+ pr_err("%s XGMAC_DMA_MODE_SWR stuck: %d\n", dev->name, ret);
goto err_stop_resets;
}
ret = xgmac->config->ops->xgmac_calibrate_pads(dev);
if (ret < 0) {
- pr_err("xgmac_calibrate_pads() failed: %d\n", ret);
+ pr_err("%s xgmac_calibrate_pads() failed: %d\n", dev->name, ret);
goto err_stop_resets;
}
@@ -485,14 +499,16 @@ static int xgmac_start(struct udevice *dev)
xgmac->phy = phy_connect(xgmac->mii, addr, dev,
xgmac->config->interface(dev));
if (!xgmac->phy) {
- pr_err("phy_connect() failed\n");
+ pr_err("%s phy_connect() failed\n", dev->name);
goto err_stop_resets;
}
if (xgmac->max_speed) {
ret = phy_set_supported(xgmac->phy, xgmac->max_speed);
if (ret) {
- pr_err("phy_set_supported() failed: %d\n", ret);
+ pr_err("%s phy_set_supported() failed: %d\n",
+ dev->name, ret);
+
goto err_shutdown_phy;
}
}
@@ -500,25 +516,25 @@ static int xgmac_start(struct udevice *dev)
xgmac->phy->node = xgmac->phy_of_node;
ret = phy_config(xgmac->phy);
if (ret < 0) {
- pr_err("phy_config() failed: %d\n", ret);
+ pr_err("%s phy_config() failed: %d\n", dev->name, ret);
goto err_shutdown_phy;
}
}
ret = phy_startup(xgmac->phy);
if (ret < 0) {
- pr_err("phy_startup() failed: %d\n", ret);
+ pr_err("%s phy_startup() failed: %d\n", dev->name, ret);
goto err_shutdown_phy;
}
if (!xgmac->phy->link) {
- pr_err("No link\n");
+ pr_err("%s No link\n", dev->name);
goto err_shutdown_phy;
}
ret = xgmac_adjust_link(dev);
if (ret < 0) {
- pr_err("xgmac_adjust_link() failed: %d\n", ret);
+ pr_err("%s xgmac_adjust_link() failed: %d\n", dev->name, ret);
goto err_shutdown_phy;
}
@@ -611,7 +627,7 @@ static int xgmac_start(struct udevice *dev)
ret = xgmac_write_hwaddr(dev);
if (ret < 0) {
- pr_err("xgmac_write_hwaddr() failed: %d\n", ret);
+ pr_err("%s xgmac_write_hwaddr() failed: %d\n", dev->name, ret);
goto err;
}
@@ -738,7 +754,7 @@ err_shutdown_phy:
err_stop_resets:
xgmac->config->ops->xgmac_stop_resets(dev);
err:
- pr_err("FAILED: %d\n", ret);
+ pr_err("%s FAILED: %d\n", dev->name, ret);
return ret;
}
@@ -1047,7 +1063,7 @@ static int xgmac_probe(struct udevice *dev)
xgmac->regs = dev_read_addr(dev);
if (xgmac->regs == FDT_ADDR_T_NONE) {
- pr_err("dev_read_addr() failed\n");
+ pr_err("%s dev_read_addr() failed\n", dev->name);
return -ENODEV;
}
xgmac->mac_regs = (void *)(xgmac->regs + XGMAC_MAC_REGS_BASE);
@@ -1058,19 +1074,23 @@ static int xgmac_probe(struct udevice *dev)
ret = xgmac_probe_resources_core(dev);
if (ret < 0) {
- pr_err("xgmac_probe_resources_core() failed: %d\n", ret);
+ pr_err("%s xgmac_probe_resources_core() failed: %d\n",
+ dev->name, ret);
+
return ret;
}
ret = xgmac->config->ops->xgmac_probe_resources(dev);
if (ret < 0) {
- pr_err("xgmac_probe_resources() failed: %d\n", ret);
+ pr_err("%s xgmac_probe_resources() failed: %d\n",
+ dev->name, ret);
+
goto err_remove_resources_core;
}
ret = xgmac->config->ops->xgmac_start_clks(dev);
if (ret < 0) {
- pr_err("xgmac_start_clks() failed: %d\n", ret);
+ pr_err("%s xgmac_start_clks() failed: %d\n", dev->name, ret);
return ret;
}
@@ -1080,7 +1100,7 @@ static int xgmac_probe(struct udevice *dev)
if (!xgmac->mii) {
xgmac->mii = mdio_alloc();
if (!xgmac->mii) {
- pr_err("mdio_alloc() failed\n");
+ pr_err("%s mdio_alloc() failed\n", dev->name);
ret = -ENOMEM;
goto err_stop_clks;
}
@@ -1091,7 +1111,9 @@ static int xgmac_probe(struct udevice *dev)
ret = mdio_register(xgmac->mii);
if (ret < 0) {
- pr_err("mdio_register() failed: %d\n", ret);
+ pr_err("%s mdio_register() failed: %d\n",
+ dev->name, ret);
+
goto err_free_mdio;
}
}
diff --git a/drivers/net/dwc_eth_xgmac_socfpga.c b/drivers/net/dwc_eth_xgmac_socfpga.c
index 87fb7e887e7..c89c8a188b7 100644
--- a/drivers/net/dwc_eth_xgmac_socfpga.c
+++ b/drivers/net/dwc_eth_xgmac_socfpga.c
@@ -29,6 +29,25 @@
#define SOCFPGA_XGMAC_SYSCON_ARG_COUNT 2
+phy_interface_t dwxgmac_of_get_mac_mode(struct udevice *dev)
+{
+ const char *mac_mode;
+ int i;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+ mac_mode = dev_read_string(dev, "mac-mode");
+ if (!mac_mode)
+ return PHY_INTERFACE_MODE_NA;
+
+ if (mac_mode) {
+ for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) {
+ if (!strcmp(mac_mode, phy_interface_strings[i]))
+ return i;
+ }
+ }
+ return PHY_INTERFACE_MODE_NA;
+}
+
static int dwxgmac_socfpga_do_setphy(struct udevice *dev, u32 modereg)
{
struct xgmac_priv *xgmac = dev_get_priv(dev);
@@ -66,12 +85,17 @@ static int xgmac_probe_resources_socfpga(struct udevice *dev)
struct ofnode_phandle_args args;
void *range;
phy_interface_t interface;
+ phy_interface_t mac_mode;
int ret;
u32 modereg;
interface = xgmac->config->interface(dev);
+ mac_mode = dwxgmac_of_get_mac_mode(dev);
+
+ if (mac_mode == PHY_INTERFACE_MODE_NA)
+ mac_mode = interface;
- switch (interface) {
+ switch (mac_mode) {
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_GMII:
modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
@@ -80,6 +104,7 @@ static int xgmac_probe_resources_socfpga(struct udevice *dev)
modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
break;
default:
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..a55f3e29f9f 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -106,6 +106,9 @@ static int dwmac_socfpga_probe(struct udevice *dev)
modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
break;
default:
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
index a6b0bafc8c6..52fa820f518 100644
--- a/drivers/net/fsl_enetc.c
+++ b/drivers/net/fsl_enetc.c
@@ -2,8 +2,10 @@
/*
* ENETC ethernet controller driver
* Copyright 2017-2021 NXP
+ * Copyright 2023-2025 NXP
*/
+#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <fdt_support.h>
@@ -16,52 +18,267 @@
#include <miiphy.h>
#include <linux/bug.h>
#include <linux/delay.h>
+#include <linux/build_bug.h>
+
+#ifdef CONFIG_ARCH_IMX9
+#include <asm/mach-imx/sys_proto.h>
+#include <cpu_func.h>
+#endif
#include "fsl_enetc.h"
#define ENETC_DRIVER_NAME "enetc_eth"
+/*
+ * Calculate number of buffer descriptors per cacheline, and compile-time
+ * validate that:
+ * - the RX and TX descriptors are the same size
+ * - the descriptors fit exactly into cachelines without overlap
+ * - all descriptors fit exactly into cachelines
+ */
+#define ENETC_NUM_BD_IN_CL \
+ ((ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd)) + \
+ BUILD_BUG_ON_ZERO(sizeof(struct enetc_tx_bd) != \
+ sizeof(union enetc_rx_bd)) + \
+ BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(struct enetc_tx_bd)) + \
+ BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(union enetc_rx_bd)) + \
+ BUILD_BUG_ON_ZERO(ENETC_BD_CNT % \
+ (ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd))))
+
static int enetc_remove(struct udevice *dev);
+static int enetc_is_imx95(struct udevice *dev)
+{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
+
+ /* Test whether this is i.MX95 ENETCv4. This may be optimized out. */
+ return IS_ENABLED(CONFIG_ARCH_IMX9) &&
+ pplat->vendor == PCI_VENDOR_ID_PHILIPS;
+}
+
+static int enetc_is_ls1028a(struct udevice *dev)
+{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
+
+ /* Test whether this is LS1028A ENETC. This may be optimized out. */
+ return IS_ENABLED(CONFIG_ARCH_LS1028A) &&
+ pplat->vendor == PCI_VENDOR_ID_FREESCALE;
+}
+
+static int enetc_dev_id(struct udevice *dev)
+{
+ if (enetc_is_imx95(dev))
+ return PCI_DEV(pci_get_devfn(dev)) >> 3;
+ if (enetc_is_ls1028a(dev))
+ return PCI_FUNC(pci_get_devfn(dev));
+
+ return 0;
+}
+
+static void enetc_inval_rxbd(struct udevice *dev)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+ union enetc_rx_bd *desc = &priv->enetc_rxbd[priv->rx_bdr.next_prod_idx];
+ unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)desc + sizeof(*desc),
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ invalidate_dcache_range(start, end);
+}
+
+static void enetc_flush_bd(struct udevice *dev, int pi, bool tx)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+ union enetc_rx_bd *rxdesc = &priv->enetc_rxbd[pi];
+ struct enetc_tx_bd *txdesc = &priv->enetc_txbd[pi];
+ unsigned long desc = tx ? (unsigned long)txdesc : (unsigned long)rxdesc;
+ unsigned long size = tx ? sizeof(*txdesc) : sizeof(*rxdesc);
+ unsigned long start = rounddown(desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup(desc + size, ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ flush_dcache_range(start, end);
+}
+
+static void enetc_inval_buffer(struct udevice *dev, void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ invalidate_dcache_range(start, end);
+}
+
+static void enetc_flush_buffer(struct udevice *dev, void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ flush_dcache_range(start, end);
+}
+
+/* register accessors */
+static u32 enetc_read_reg(void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static void enetc_write_reg(void __iomem *addr, u32 val)
+{
+ writel(val, addr);
+}
+
+static void enetc_write(struct enetc_priv *priv, u32 off, u32 val)
+{
+ enetc_write_reg(priv->regs_base + off, val);
+}
+
+/* base port register accessors */
+static void enetc_write_pmr(struct udevice *dev, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PMR + data->reg_offset_pmr;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+static void enetc_write_psipmar(struct udevice *dev, int n, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = (n ? ENETC_PSIPMAR1 : ENETC_PSIPMAR0) +
+ data->reg_offset_psipmar;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* port station register accessors */
+static void enetc_write_psicfgr(struct udevice *dev, int port, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PSICFGR(port, ENETC_PSICFGR_SHIFT_LS) +
+ data->reg_offset_psicfgr;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* port register accessors */
+static u32 enetc_read_pcapr_mdio(struct udevice *dev)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PCAPR0 + data->reg_offset_pcapr;
+ const u32 reg = enetc_read_reg(priv->port_regs + off);
+
+ if (enetc_is_imx95(dev))
+ return reg & ENETC_PCS_PROT;
+ else if (enetc_is_ls1028a(dev))
+ return reg & ENETC_PCAPRO_MDIO;
+
+ return 0;
+}
+
+static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val)
+{
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* MAC port register accessors */
+static u32 enetc_read_mac_port(struct udevice *dev, u32 off)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_read_reg(priv->port_regs + data->reg_offset_mac + off);
+}
+
+static void enetc_write_mac_port(struct udevice *dev, u32 off, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ enetc_write_reg(priv->port_regs + data->reg_offset_mac + off, val);
+}
+
+/* BDR register accessor, see also ENETC_BDR() */
+static void enetc_bdr_write(struct enetc_priv *priv, int type, int n,
+ u32 off, u32 val)
+{
+ enetc_write(priv, ENETC_BDR(type, n, off), val);
+}
+
/*
* sets the MAC address in IERB registers, this setting is persistent and
* carried over to Linux.
*/
-static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn,
- const u8 *enetaddr)
-{
-#ifdef CONFIG_ARCH_LS1028A
-/*
- * LS1028A is the only part with IERB at this time and there are plans to change
- * its structure, keep this LS1028A specific for now
- */
#define IERB_BASE 0x1f0800000ULL
#define IERB_PFMAC(pf, vf, n) (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \
+ (n) * 4)
-static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
-
+static void enetc_set_ierb_primary_mac(struct udevice *dev, void *blob)
+{
+ static int ierb_fn_to_pf[] = { 0, 1, 2, -1, -1, -1, 3 };
+ struct pci_child_plat *ppdata = dev_get_parent_plat(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u8 *enetaddr = pdata->enetaddr;
u16 lower = *(const u16 *)(enetaddr + 4);
u32 upper = *(const u32 *)enetaddr;
+ int devfn, offset;
+ char path[256];
- if (ierb_fn_to_pf[devfn] < 0)
+ if (enetc_is_imx95(dev)) {
+ /*
+ * Configure the ENETC primary MAC addresses - Set register
+ * PMAR0/1 for SI 0 and PSIaPMAR0/1 for SI 1, 2 .. a
+ * (optionally pre-configured in IERB).
+ */
+ devfn = enetc_dev_id(dev);
+ if (devfn > 2)
+ return;
+
+ enetc_write(priv, IMX95_ENETC_SIPMAR0, upper);
+ enetc_write(priv, IMX95_ENETC_SIPMAR1, lower);
+
+ snprintf(path, 256, "/soc/pcie@%x/ethernet@%x,%x",
+ PCI_BUS(dm_pci_get_bdf(dev)), PCI_DEV(ppdata->devfn),
+ PCI_FUNC(ppdata->devfn));
+ } else if (enetc_is_ls1028a(dev)) {
+ /*
+ * LS1028A is the only part with IERB at this time and
+ * there are plans to change its structure, keep this
+ * LS1028A specific for now.
+ */
+ devfn = PCI_FUNC(ppdata->devfn);
+
+ if (ierb_fn_to_pf[devfn] < 0)
+ return;
+
+ out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
+ out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
+
+ snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
+ PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
+ } else {
return;
+ }
- out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
- out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
-#endif
+ offset = fdt_path_offset(blob, path);
+ if (offset >= 0)
+ fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
}
/* sets up primary MAC addresses in DT/IERB */
void fdt_fixup_enetc_mac(void *blob)
{
- struct pci_child_plat *ppdata;
- struct eth_pdata *pdata;
struct udevice *dev;
struct uclass *uc;
- char path[256];
- int offset;
- int devfn;
uclass_get(UCLASS_ETH, &uc);
uclass_foreach_dev(dev, uc) {
@@ -69,18 +286,7 @@ void fdt_fixup_enetc_mac(void *blob)
strcmp(dev->driver->name, ENETC_DRIVER_NAME))
continue;
- pdata = dev_get_plat(dev);
- ppdata = dev_get_parent_plat(dev);
- devfn = PCI_FUNC(ppdata->devfn);
-
- enetc_set_ierb_primary_mac(dev, devfn, pdata->enetaddr);
-
- snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
- PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
- offset = fdt_path_offset(blob, path);
- if (offset < 0)
- continue;
- fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
+ enetc_set_ierb_primary_mac(dev, blob);
}
}
@@ -101,7 +307,7 @@ static int enetc_bind(struct udevice *dev)
* PCI function # and enetc#N based on interface count
*/
if (ofnode_valid(dev_ofnode(dev)))
- sprintf(name, "enetc-%u", PCI_FUNC(pci_get_devfn(dev)));
+ sprintf(name, "enetc-%u", enetc_dev_id(dev));
else
sprintf(name, "enetc#%u", eth_num_devices++);
device_set_name(dev, name);
@@ -181,10 +387,9 @@ static int enetc_init_sgmii(struct udevice *dev)
/* set up MAC for RGMII */
static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
{
- struct enetc_priv *priv = dev_get_priv(dev);
- u32 old_val, val;
+ u32 old_val, val, dpx = 0;
- old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE);
+ old_val = val = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
/* disable unreliable RGMII in-band signaling and force the MAC into
* the speed negotiated by the PHY.
@@ -202,15 +407,20 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
val |= ENETC_PM_IFM_SSP_10;
}
+ if (enetc_is_imx95(dev))
+ dpx = ENETC_PM_IFM_FULL_DPX_IMX;
+ else if (enetc_is_ls1028a(dev))
+ dpx = ENETC_PM_IFM_FULL_DPX_LS;
+
if (phydev->duplex == DUPLEX_FULL)
- val |= ENETC_PM_IFM_FULL_DPX;
+ val |= dpx;
else
- val &= ~ENETC_PM_IFM_FULL_DPX;
+ val &= ~dpx;
if (val == old_val)
return;
- enetc_write_port(priv, ENETC_PM_IF_MODE, val);
+ enetc_write_mac_port(dev, ENETC_PM_IF_MODE, val);
}
/* set up MAC configuration for the given interface type */
@@ -230,9 +440,12 @@ static void enetc_setup_mac_iface(struct udevice *dev,
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
/* set ifmode to (US)XGMII */
- if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
- if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
- enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+ if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
+ if (enetc_is_imx95(dev))
+ if_mode &= ~ENETC_PM_IF_IFMODE_MASK_IMX;
+ else if (enetc_is_ls1028a(dev))
+ if_mode &= ~ENETC_PM_IF_IFMODE_MASK_LS;
+ enetc_write_mac_port(dev, ENETC_PM_IF_MODE, if_mode);
break;
};
}
@@ -263,7 +476,7 @@ static void enetc_start_pcs(struct udevice *dev)
struct enetc_priv *priv = dev_get_priv(dev);
/* register internal MDIO for debug purposes */
- if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) {
+ if (enetc_read_pcapr_mdio(dev)) {
priv->imdio.read = enetc_mdio_read;
priv->imdio.write = enetc_mdio_write;
priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
@@ -375,6 +588,21 @@ static int enetc_remove(struct udevice *dev)
return 0;
}
+static int enetc_imx95_write_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *plat = dev_get_plat(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ u8 *addr = plat->enetaddr;
+
+ u16 lower = *(const u16 *)(addr + 4);
+ u32 upper = *(const u32 *)addr;
+
+ enetc_write_port(priv, IMX95_ENETC_PMAR0, upper);
+ enetc_write_port(priv, IMX95_ENETC_PMAR1, lower);
+
+ return 0;
+}
+
/*
* LS1028A is the only part with IERB at this time and there are plans to
* change its structure, keep this LS1028A specific for now.
@@ -413,39 +641,46 @@ static int enetc_ls1028a_write_hwaddr(struct udevice *dev)
static int enetc_write_hwaddr(struct udevice *dev)
{
struct eth_pdata *plat = dev_get_plat(dev);
- struct enetc_priv *priv = dev_get_priv(dev);
u8 *addr = plat->enetaddr;
- if (IS_ENABLED(CONFIG_ARCH_LS1028A))
+ if (enetc_is_imx95(dev))
+ return enetc_imx95_write_hwaddr(dev);
+ if (enetc_is_ls1028a(dev))
return enetc_ls1028a_write_hwaddr(dev);
u16 lower = *(const u16 *)(addr + 4);
u32 upper = *(const u32 *)addr;
- enetc_write_port(priv, ENETC_PSIPMAR0, upper);
- enetc_write_port(priv, ENETC_PSIPMAR1, lower);
+ enetc_write_psipmar(dev, 0, upper);
+ enetc_write_psipmar(dev, 1, lower);
return 0;
}
/* Configure port parameters (# of rings, frame size, enable port) */
-static void enetc_enable_si_port(struct enetc_priv *priv)
+static void enetc_enable_si_port(struct udevice *dev)
{
- u32 val;
+ struct enetc_priv *priv = dev_get_priv(dev);
+ u32 val = ENETC_PM_CC_TXP_IMX | ENETC_PM_CC_TX | ENETC_PM_CC_RX;
/* set Rx/Tx BDR count */
- val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT);
- val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT);
- enetc_write_port(priv, ENETC_PSICFGR(0), val);
+ enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT,
+ ENETC_TX_BDR_CNT));
/* set Rx max frame size */
- enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
+ enetc_write_mac_port(dev, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
/* enable MAC port */
- enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN);
+ if (enetc_is_ls1028a(dev))
+ val |= ENETC_PM_CC_TXP_LS | ENETC_PM_CC_PROMIS;
+ enetc_write_mac_port(dev, ENETC_PM_CC, val);
/* enable port */
- enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN);
+ if (enetc_is_imx95(dev))
+ enetc_write_port(priv, ENETC_POR, 0x0);
+ enetc_write_pmr(dev, ENETC_PMR_SI0_EN);
/* set SI cache policy */
- enetc_write(priv, ENETC_SICAR0,
- ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG);
+ enetc_write(priv, ENETC_SICAR0, ENETC_SICAR_WR_CFG |
+ (enetc_is_imx95(dev) ?
+ ENETC_SICAR_RD_CFG_IMX :
+ ENETC_SICAR_RD_CFG_LS));
/* enable SI */
enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN);
}
@@ -536,6 +771,8 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
priv->enetc_rxbd[i].w.addr = enetc_rxb_address(dev, i);
/* each RX buffer must be aligned to 64B */
WARN_ON(priv->enetc_rxbd[i].w.addr & (ARCH_DMA_MINALIGN - 1));
+
+ enetc_flush_bd(dev, i, false);
}
/* reset producer (ENETC owned) and consumer (SW owned) index */
@@ -556,6 +793,7 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
*/
static int enetc_start(struct udevice *dev)
{
+ int ret;
struct enetc_priv *priv = dev_get_priv(dev);
/* reset and enable the PCI device */
@@ -563,15 +801,19 @@ static int enetc_start(struct udevice *dev)
dm_pci_clrset_config16(dev, PCI_COMMAND, 0,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- enetc_enable_si_port(priv);
+ enetc_enable_si_port(dev);
/* setup Tx/Rx buffer descriptors */
enetc_setup_tx_bdr(dev);
enetc_setup_rx_bdr(dev);
+ ret = phy_startup(priv->phy);
+ if (ret)
+ return ret;
+
enetc_setup_mac_iface(dev, priv->phy);
- return phy_startup(priv->phy);
+ return 0;
}
/*
@@ -614,6 +856,8 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
enetc_dbg(dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length,
upper_32_bits((u64)nv_packet), lower_32_bits((u64)nv_packet));
+ enetc_flush_buffer(dev, packet, length);
+
/* prepare Tx BD */
memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd));
priv->enetc_txbd[pi].addr =
@@ -621,7 +865,10 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
priv->enetc_txbd[pi].buf_len = cpu_to_le16(length);
priv->enetc_txbd[pi].frm_len = cpu_to_le16(length);
priv->enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F);
+
dmb();
+ enetc_flush_bd(dev, pi, true);
+
/* send frame: increment producer index */
pi = (pi + 1) % txr->bd_count;
txr->next_prod_idx = pi;
@@ -643,15 +890,15 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct enetc_priv *priv = dev_get_priv(dev);
struct bd_ring *rxr = &priv->rx_bdr;
- int tries = ENETC_POLL_TRIES;
int pi = rxr->next_prod_idx;
- int ci = rxr->next_cons_idx;
+ int tries = ENETC_POLL_TRIES;
u32 status;
int len;
u8 rdy;
do {
dmb();
+ enetc_inval_rxbd(dev);
status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus);
/* check if current BD is ready to be consumed */
rdy = ENETC_RXBD_STATUS_R(status);
@@ -663,45 +910,162 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
dmb();
len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len);
*packetp = (uchar *)enetc_rxb_address(dev, pi);
+ enetc_inval_buffer(dev, *packetp, len);
enetc_dbg(dev, "RxBD[%d]: len=%d err=%d pkt=0x%x%08x\n", pi, len,
ENETC_RXBD_STATUS_ERRORS(status),
upper_32_bits((u64)*packetp), lower_32_bits((u64)*packetp));
- /* BD clean up and advance to next in ring */
- memset(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd));
- priv->enetc_rxbd[pi].w.addr = enetc_rxb_address(dev, pi);
+ return len;
+}
+
+static int enetc_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+ const int bd_num_in_cl = enetc_is_imx95(dev) ? ENETC_NUM_BD_IN_CL : 1;
+ struct enetc_priv *priv = dev_get_priv(dev);
+ struct bd_ring *rxr = &priv->rx_bdr;
+ int pi = rxr->next_prod_idx;
+ int ci = rxr->next_cons_idx;
+ uchar *packet_expected;
+ int i;
+
+ packet_expected = (uchar *)enetc_rxb_address(dev, pi);
+ if (packet != packet_expected) {
+ printf("%s: Unexpected packet (expected %p)\n", __func__,
+ packet_expected);
+ return -EINVAL;
+ }
+
rxr->next_prod_idx = (pi + 1) % rxr->bd_count;
ci = (ci + 1) % rxr->bd_count;
rxr->next_cons_idx = ci;
dmb();
- /* free up the slot in the ring for HW */
- enetc_write_reg(rxr->cons_idx, ci);
- return len;
+ if ((pi + 1) % bd_num_in_cl == 0) {
+ /* BD clean up and advance to next in ring */
+ for (i = 0; i < bd_num_in_cl; i++) {
+ memset(&priv->enetc_rxbd[pi - i], 0, sizeof(union enetc_rx_bd));
+ priv->enetc_rxbd[pi - i].w.addr = enetc_rxb_address(dev, pi - i);
+ }
+
+ /* Will flush all bds in one cacheline */
+ enetc_flush_bd(dev, pi - bd_num_in_cl + 1, false);
+
+ /* free up the slot in the ring for HW */
+ enetc_write_reg(rxr->cons_idx, ci);
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_ARCH_IMX9)
+static int enetc_read_rom_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ unsigned int dev_id = enetc_dev_id(dev);
+ unsigned char *mac = pdata->enetaddr;
+
+ if (dev_id > 2)
+ return -EINVAL;
+
+ imx_get_mac_from_fuse(dev_id, mac);
+
+ return !is_valid_ethaddr(mac);
}
-static const struct eth_ops enetc_ops = {
+static const struct eth_ops enetc_ops_imx = {
+ .start = enetc_start,
+ .send = enetc_send,
+ .recv = enetc_recv,
+ .stop = enetc_stop,
+ .free_pkt = enetc_free_pkt,
+ .write_hwaddr = enetc_write_hwaddr,
+ .read_rom_hwaddr = enetc_read_rom_hwaddr,
+};
+
+static int enetc_probe_imx(struct udevice *dev)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_optional(dev, "ref");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = clk_enable(clk);
+ if (ret)
+ return ret;
+
+ ret = enetc_probe(dev);
+ if (ret)
+ clk_disable(clk);
+
+ return ret;
+}
+
+U_BOOT_DRIVER(eth_enetc_imx) = {
+ .name = ENETC_DRIVER_NAME,
+ .id = UCLASS_ETH,
+ .bind = enetc_bind,
+ .probe = enetc_probe_imx,
+ .remove = enetc_remove,
+ .ops = &enetc_ops_imx,
+ .priv_auto = sizeof(struct enetc_priv),
+ .plat_auto = sizeof(struct eth_pdata),
+};
+
+static const struct enetc_data enetc_data_imx = {
+ .reg_offset_pmr = ENETC_PMR_OFFSET_IMX,
+ .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_IMX,
+ .reg_offset_pcapr = ENETC_PCAPR_OFFSET_IMX,
+ .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_IMX,
+ .reg_offset_mac = ENETC_PM_OFFSET_IMX,
+};
+
+static struct pci_device_id enetc_ids_imx[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_ETH),
+ .driver_data = (ulong)&enetc_data_imx,
+ },
+ {}
+};
+
+U_BOOT_PCI_DEVICE(eth_enetc_imx, enetc_ids_imx);
+#endif
+
+static const struct eth_ops enetc_ops_ls = {
.start = enetc_start,
.send = enetc_send,
.recv = enetc_recv,
.stop = enetc_stop,
+ .free_pkt = enetc_free_pkt,
.write_hwaddr = enetc_write_hwaddr,
};
-U_BOOT_DRIVER(eth_enetc) = {
+U_BOOT_DRIVER(eth_enetc_ls) = {
.name = ENETC_DRIVER_NAME,
.id = UCLASS_ETH,
.bind = enetc_bind,
.probe = enetc_probe,
.remove = enetc_remove,
- .ops = &enetc_ops,
+ .ops = &enetc_ops_ls,
.priv_auto = sizeof(struct enetc_priv),
.plat_auto = sizeof(struct eth_pdata),
};
-static struct pci_device_id enetc_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) },
+static const struct enetc_data enetc_data_ls = {
+ .reg_offset_pmr = ENETC_PMR_OFFSET_LS,
+ .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_LS,
+ .reg_offset_pcapr = ENETC_PCAPR_OFFSET_LS,
+ .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_LS,
+ .reg_offset_mac = ENETC_PM_OFFSET_LS,
+};
+
+static struct pci_device_id enetc_ids_ls[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH),
+ .driver_data = (ulong)&enetc_data_ls,
+ },
{}
};
-U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids);
+U_BOOT_PCI_DEVICE(eth_enetc_ls, enetc_ids_ls);
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
index f2acf367aa3..804df853bf5 100644
--- a/drivers/net/fsl_enetc.h
+++ b/drivers/net/fsl_enetc.h
@@ -12,7 +12,9 @@
/* PCI function IDs */
#define PCI_DEVICE_ID_ENETC_ETH 0xE100
+#define PCI_DEVICE_ID_ENETC4_ETH 0xE101
#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
+#define PCI_DEVICE_ID_ENETC4_EMDIO 0xEE00
/* ENETC Ethernet controller registers */
/* Station interface register offsets */
@@ -22,7 +24,8 @@
/* write cache cfg: snoop, no allocate, data & BD coherent */
#define ENETC_SICAR_WR_CFG 0x6767
/* read cache cfg: coherent copy, look up, don't alloc in cache */
-#define ENETC_SICAR_RD_CFG 0x27270000
+#define ENETC_SICAR_RD_CFG_LS 0x27270000
+#define ENETC_SICAR_RD_CFG_IMX 0x2b2b0000
#define ENETC_SIROCT 0x300
#define ENETC_SIRFRM 0x308
#define ENETC_SITOCT 0x320
@@ -57,32 +60,60 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PORT_REGS_OFF 0x10000
/* Port registers */
+#define ENETC_PMR_OFFSET_IMX 0x0010
+#define ENETC_PMR_OFFSET_LS 0x0000
#define ENETC_PMR 0x0000
#define ENETC_PMR_SI0_EN BIT(16)
#define ENETC_PSIPMMR 0x0018
-#define ENETC_PSIPMAR0 0x0100
-#define ENETC_PSIPMAR1 0x0104
-#define ENETC_PCAPR0 0x0900
-#define ENETC_PCAPRO_MDIO BIT(11)
-#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10)
-#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff)
-#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16)
+#define ENETC_PSIPMARn_OFFSET_IMX 0x0000
+#define ENETC_PSIPMARn_OFFSET_LS 0x0080
+#define ENETC_PSIPMAR0 0x0080
+#define ENETC_PSIPMAR1 0x0084
+#define ENETC_PCAPR_OFFSET_IMX 0x4008
+#define ENETC_PCAPR_OFFSET_LS 0x0900
+#define ENETC_PCAPR0 0x0000
+#define ENETC_PCAPRO_MDIO BIT(11) /* LS only */
+#define ENETC_PCS_PROT GENMASK(15, 0) /* IMX only */
+/* ENETC base registers */
+#define ENETC_PSICFGR_OFFSET_LS 0x0940
+#define ENETC_PSICFGR_SHIFT_LS 0x10
+#define ENETC_PSICFGR_OFFSET_IMX 0x2010
+#define ENETC_PSICFGR_SHIFT_IMX 0x80
+#define ENETC_PSICFGR(n, s) ((n) * (s))
+#define ENETC_PSICFGR_SET_BDR(rx, tx) (((rx) << 16) | (tx))
/* MAC configuration */
-#define ENETC_PM_CC 0x8008
+#define ENETC_PM_OFFSET_IMX 0x5000
+#define ENETC_PM_OFFSET_LS 0x8000
+#define ENETC_PM_CC 0x0008
#define ENETC_PM_CC_DEFAULT 0x0810
-#define ENETC_PM_CC_RX_TX_EN 0x8813
-#define ENETC_PM_MAXFRM 0x8014
+#define ENETC_PM_CC_TXP_IMX BIT(15)
+#define ENETC_PM_CC_TXP_LS BIT(11)
+#define ENETC_PM_CC_PROMIS BIT(4)
+#define ENETC_PM_CC_TX BIT(1)
+#define ENETC_PM_CC_RX BIT(0)
+#define ENETC_PM_MAXFRM 0x0014
#define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN
-#define ENETC_PM_IMDIO_BASE 0x8030
-#define ENETC_PM_IF_MODE 0x8300
+#define ENETC_PM_IMDIO_BASE 0x0030
+#define ENETC_PM_IF_MODE 0x0300
#define ENETC_PM_IF_MODE_RG BIT(2)
#define ENETC_PM_IF_MODE_AN_ENA BIT(15)
#define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13)
#define ENETC_PM_IFM_SSP_1000 (2 << 13)
#define ENETC_PM_IFM_SSP_100 (0 << 13)
#define ENETC_PM_IFM_SSP_10 (1 << 13)
-#define ENETC_PM_IFM_FULL_DPX BIT(12)
-#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0)
+#define ENETC_PM_IFM_FULL_DPX_IMX BIT(6)
+#define ENETC_PM_IFM_FULL_DPX_LS BIT(12)
+#define ENETC_PM_IF_IFMODE_MASK_IMX GENMASK(2, 0)
+#define ENETC_PM_IF_IFMODE_MASK_LS GENMASK(1, 0)
+
+/* i.MX95 specific registers */
+#define IMX95_ENETC_SIPMAR0 0x80
+#define IMX95_ENETC_SIPMAR1 0x84
+
+/* Port registers */
+#define IMX95_ENETC_PMAR0 0x4020
+#define IMX95_ENETC_PMAR1 0x4024
+#define ENETC_POR 0x4100
/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */
#define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER
@@ -163,25 +194,14 @@ struct enetc_priv {
struct phy_device *phy;
};
-/* register accessors */
-#define enetc_read_reg(x) readl((x))
-#define enetc_write_reg(x, val) writel((val), (x))
-#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off))
-#define enetc_write(priv, off, v) \
- enetc_write_reg((priv)->regs_base + (off), v)
-
-/* port register accessors */
-#define enetc_port_regs(priv, off) ((priv)->port_regs + (off))
-#define enetc_read_port(priv, off) \
- enetc_read_reg(enetc_port_regs((priv), (off)))
-#define enetc_write_port(priv, off, v) \
- enetc_write_reg(enetc_port_regs((priv), (off)), v)
-
-/* BDR register accessors, see ENETC_BDR() */
-#define enetc_bdr_read(priv, t, n, off) \
- enetc_read(priv, ENETC_BDR(t, n, off))
-#define enetc_bdr_write(priv, t, n, off, val) \
- enetc_write(priv, ENETC_BDR(t, n, off), val)
+struct enetc_data {
+ /* Register layout offsets */
+ u16 reg_offset_pmr;
+ u16 reg_offset_psipmar;
+ u16 reg_offset_pcapr;
+ u16 reg_offset_psicfgr;
+ u16 reg_offset_mac;
+};
/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
#define ENETC_PCS_PHY_ADDR 0
diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c
index 2d5fcbb6dbd..c1d491f2c5a 100644
--- a/drivers/net/fsl_enetc_mdio.c
+++ b/drivers/net/fsl_enetc_mdio.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ENETC ethernet controller driver
- * Copyright 2019 NXP
+ * Copyright 2019-2025 NXP
*/
#include <dm.h>
@@ -14,6 +14,16 @@
#include "fsl_enetc.h"
+static u32 enetc_read(struct enetc_mdio_priv *priv, u32 off)
+{
+ return readl(priv->regs_base + off);
+}
+
+static void enetc_write(struct enetc_mdio_priv *priv, u32 off, u32 val)
+{
+ writel(val, priv->regs_base + off);
+}
+
static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
{
int to = 10000;
@@ -122,7 +132,9 @@ static int enetc_mdio_bind(struct udevice *dev)
static int enetc_mdio_probe(struct udevice *dev)
{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
struct enetc_mdio_priv *priv = dev_get_priv(dev);
+ u16 cmd = PCI_COMMAND_MEMORY;
priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, 0);
if (!priv->regs_base) {
@@ -132,7 +144,10 @@ static int enetc_mdio_probe(struct udevice *dev)
priv->regs_base += ENETC_MDIO_BASE;
- dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
+ if (pplat->vendor == PCI_VENDOR_ID_PHILIPS) /* i.MX95 */
+ cmd |= PCI_COMMAND_MASTER;
+
+ dm_pci_clrset_config16(dev, PCI_COMMAND, 0, cmd);
return 0;
}
@@ -148,6 +163,7 @@ U_BOOT_DRIVER(enetc_mdio) = {
static struct pci_device_id enetc_mdio_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
+ { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_EMDIO) },
{ }
};
diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c
new file mode 100644
index 00000000000..46b68d3d8a4
--- /dev/null
+++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * NXP NETC Blocks Control Driver
+ *
+ * Copyright 2024 NXP
+ *
+ * This driver is used for pre-initialization of NETC, such as PCS and MII
+ * protocols, LDID, warm reset, etc. Therefore, all NETC device drivers can
+ * only be probed after the netc-blk-crtl driver has completed initialization.
+ * In addition, when the system enters suspend mode, IERB, PRB, and NETCMIX
+ * will be powered off, except for WOL. Therefore, when the system resumes,
+ * these blocks need to be reinitialized.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/iopoll.h>
+#include <phy_interface.h>
+
+/* NETCMIX registers */
+#define IMX95_CFG_LINK_IO_VAR 0x0
+#define IO_VAR_16FF_16G_SERDES 0x1
+#define IO_VAR(port, var) (((var) & 0xf) << ((port) << 2))
+
+#define IMX95_CFG_LINK_MII_PROT 0x4
+#define CFG_LINK_MII_PORT_0 GENMASK(3, 0)
+#define CFG_LINK_MII_PORT_1 GENMASK(7, 4)
+#define MII_PROT_MII 0x0
+#define MII_PROT_RMII 0x1
+#define MII_PROT_RGMII 0x2
+#define MII_PROT_SERIAL 0x3
+#define MII_PROT(port, prot) (((prot) & 0xf) << ((port) << 2))
+
+#define IMX95_CFG_LINK_PCS_PROT(a) (0x8 + (a) * 4)
+#define PCS_PROT_1G_SGMII BIT(0)
+#define PCS_PROT_2500M_SGMII BIT(1)
+#define PCS_PROT_XFI BIT(3)
+#define PCS_PROT_SFI BIT(4)
+#define PCS_PROT_10G_SXGMII BIT(6)
+
+/* NETC privileged register block register */
+#define PRB_NETCRR 0x100
+#define NETCRR_SR BIT(0)
+#define NETCRR_LOCK BIT(1)
+
+#define PRB_NETCSR 0x104
+#define NETCSR_ERROR BIT(0)
+#define NETCSR_STATE BIT(1)
+
+/* NETC integrated endpoint register block register */
+#define IERB_EMDIOFAUXR 0x344
+#define IERB_T0FAUXR 0x444
+#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a))
+#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a))
+#define FAUXR_LDID GENMASK(3, 0)
+
+/* Platform information */
+#define IMX95_ENETC0_BUS_DEVFN 0x0
+#define IMX95_ENETC1_BUS_DEVFN 0x40
+#define IMX95_ENETC2_BUS_DEVFN 0x80
+
+/* Flags for different platforms */
+#define NETC_HAS_NETCMIX BIT(0)
+
+struct netc_blk_ctrl {
+ void __iomem *prb;
+ void __iomem *ierb;
+ void __iomem *netcmix;
+};
+
+static void netc_reg_write(void __iomem *base, u32 offset, u32 val)
+{
+ writel(val, base + offset);
+}
+
+static u32 netc_reg_read(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static int netc_of_pci_get_bus_devfn(ofnode node)
+{
+ u32 reg[5];
+ int error;
+
+ error = ofnode_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
+ if (error)
+ return error;
+
+ return (reg[0] >> 8) & 0xffff;
+}
+
+static int netc_get_link_mii_protocol(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_MII:
+ return MII_PROT_MII;
+ case PHY_INTERFACE_MODE_RMII:
+ return MII_PROT_RMII;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ return MII_PROT_RGMII;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ return MII_PROT_SERIAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx95_netcmix_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ ofnode child, gchild;
+ phy_interface_t interface;
+ int bus_devfn, mii_proto;
+ u32 val;
+
+ /* Default setting of MII protocol */
+ val = MII_PROT(0, MII_PROT_RGMII) | MII_PROT(1, MII_PROT_RGMII) |
+ MII_PROT(2, MII_PROT_SERIAL);
+
+ /* Update the link MII protocol through parsing phy-mode */
+ dev_for_each_subnode(child, dev) {
+ if (!ofnode_is_enabled(child))
+ continue;
+
+ ofnode_for_each_subnode(gchild, child) {
+ if (!ofnode_is_enabled(gchild))
+ continue;
+
+ if (!ofnode_device_is_compatible(gchild, "pci1131,e101"))
+ continue;
+
+ bus_devfn = netc_of_pci_get_bus_devfn(gchild);
+ if (bus_devfn < 0)
+ return -EINVAL;
+
+ if (bus_devfn == IMX95_ENETC2_BUS_DEVFN)
+ continue;
+
+ interface = ofnode_read_phy_mode(gchild);
+ if (interface == -1)
+ continue;
+
+ mii_proto = netc_get_link_mii_protocol(interface);
+ if (mii_proto < 0)
+ return -EINVAL;
+
+ switch (bus_devfn) {
+ case IMX95_ENETC0_BUS_DEVFN:
+ val &= ~CFG_LINK_MII_PORT_0;
+ val |= FIELD_PREP(CFG_LINK_MII_PORT_0, mii_proto);
+ break;
+ case IMX95_ENETC1_BUS_DEVFN:
+ val &= ~CFG_LINK_MII_PORT_1;
+ val |= FIELD_PREP(CFG_LINK_MII_PORT_1, mii_proto);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Configure Link I/O variant */
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_IO_VAR,
+ IO_VAR(2, IO_VAR_16FF_16G_SERDES));
+ /* Configure Link 2 PCS protocol */
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_PCS_PROT(2),
+ PCS_PROT_10G_SXGMII);
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_MII_PROT, val);
+
+ return 0;
+}
+
+static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
+{
+ return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
+}
+
+static int netc_lock_ierb(struct netc_blk_ctrl *priv)
+{
+ u32 val;
+
+ netc_reg_write(priv->prb, PRB_NETCRR, NETCRR_LOCK);
+
+ return readl_poll_timeout(priv->prb + PRB_NETCSR, val,
+ !(val & NETCSR_STATE), 2000);
+}
+
+static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv)
+{
+ u32 val;
+
+ netc_reg_write(priv->prb, PRB_NETCRR, 0);
+
+ return readl_poll_timeout(priv->prb + PRB_NETCRR, val,
+ !(val & NETCRR_LOCK), 100000);
+}
+
+static int imx95_ierb_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+
+ /* EMDIO : No MSI-X intterupt */
+ netc_reg_write(priv->ierb, IERB_EMDIOFAUXR, 0);
+ /* ENETC0 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(0), 0);
+ /* ENETC0 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(0), 1);
+ /* ENETC0 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(1), 2);
+ /* ENETC1 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(1), 3);
+ /* ENETC1 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(2), 5);
+ /* ENETC1 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(3), 6);
+ /* ENETC2 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(2), 4);
+ /* ENETC2 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(4), 5);
+ /* ENETC2 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(5), 6);
+ /* NETC TIMER */
+ netc_reg_write(priv->ierb, IERB_T0FAUXR, 7);
+
+ return 0;
+}
+
+static int netc_ierb_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ int err;
+
+ if (netc_ierb_is_locked(priv)) {
+ err = netc_unlock_ierb_with_warm_reset(priv);
+ if (err) {
+ dev_err(dev, "Unlock IERB failed.\n");
+ return err;
+ }
+ }
+
+ err = imx95_ierb_init(dev);
+ if (err)
+ return err;
+
+ err = netc_lock_ierb(priv);
+ if (err) {
+ dev_err(dev, "Lock IERB failed.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int netc_prb_check_error(struct netc_blk_ctrl *priv)
+{
+ if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR)
+ return -1;
+
+ return 0;
+}
+
+static const struct udevice_id netc_blk_ctrl_match[] = {
+ { .compatible = "nxp,imx95-netc-blk-ctrl" },
+ {},
+};
+
+static int netc_blk_ctrl_probe(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ struct clk *ipg_clk;
+ fdt_addr_t regs;
+ int err;
+
+ ipg_clk = devm_clk_get_optional(dev, "ipg");
+ if (IS_ERR(ipg_clk)) {
+ dev_err(dev, "Set ipg clock failed\n");
+ return PTR_ERR(ipg_clk);
+ }
+
+ err = clk_prepare_enable(ipg_clk);
+ if (err) {
+ dev_err(dev, "Enable ipg clock failed\n");
+ return PTR_ERR(ipg_clk);
+ }
+
+ regs = dev_read_addr_name(dev, "ierb");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing IERB resource\n");
+ return -EINVAL;
+ }
+
+ priv->ierb = (void __iomem *)regs;
+ regs = dev_read_addr_name(dev, "prb");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing PRB resource\n");
+ return -EINVAL;
+ }
+
+ priv->prb = (void __iomem *)regs;
+ regs = dev_read_addr_name(dev, "netcmix");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing NETCMIX resource\n");
+ return -EINVAL;
+ }
+
+ priv->netcmix = (void __iomem *)regs;
+
+ err = imx95_netcmix_init(dev);
+ if (err) {
+ dev_err(dev, "Initializing NETCMIX failed\n");
+ return err;
+ }
+
+ err = netc_ierb_init(dev);
+ if (err) {
+ dev_err(dev, "Initializing IERB failed\n");
+ return err;
+ }
+
+ if (netc_prb_check_error(priv) < 0)
+ dev_warn(dev, "The current IERB configuration is invalid\n");
+
+ return 0;
+}
+
+U_BOOT_DRIVER(netc_blk_ctrl_drv) = {
+ .name = "netc_blk_ctrl",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = netc_blk_ctrl_match,
+ .probe = netc_blk_ctrl_probe,
+ .priv_auto = sizeof(struct netc_blk_ctrl),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/net/mtk_eth/Kconfig b/drivers/net/mtk_eth/Kconfig
new file mode 100644
index 00000000000..e8cdf408237
--- /dev/null
+++ b/drivers/net/mtk_eth/Kconfig
@@ -0,0 +1,39 @@
+
+config MEDIATEK_ETH
+ bool "MediaTek Ethernet GMAC Driver"
+ select PHYLIB
+ select DM_GPIO
+ select DM_RESET
+ help
+ This Driver support MediaTek Ethernet GMAC
+ Say Y to enable support for the MediaTek Ethernet GMAC.
+
+if MEDIATEK_ETH
+
+config MTK_ETH_SGMII
+ bool
+ default y if ARCH_MEDIATEK && !TARGET_MT7623
+
+config MTK_ETH_XGMII
+ bool
+ default y if TARGET_MT7987 || TARGET_MT7988
+
+config MTK_ETH_SWITCH_MT7530
+ bool "Support for MediaTek MT7530 ethernet switch"
+ default y if TARGET_MT7623 || SOC_MT7621
+
+config MTK_ETH_SWITCH_MT7531
+ bool "Support for MediaTek MT7531 ethernet switch"
+ default y if TARGET_MT7622 || TARGET_MT7629 || TARGET_MT7981 || \
+ TARGET_MT7986 || TARGET_MT7987
+
+config MTK_ETH_SWITCH_MT7988
+ bool "Support for MediaTek MT7988 built-in ethernet switch"
+ depends on TARGET_MT7988
+ default y
+
+config MTK_ETH_SWITCH_AN8855
+ bool "Support for Airoha AN8855 ethernet switch"
+ default y if TARGET_MT7981 || TARGET_MT7987
+
+endif # MEDIATEK_ETH
diff --git a/drivers/net/mtk_eth/Makefile b/drivers/net/mtk_eth/Makefile
new file mode 100644
index 00000000000..a342325ed5d
--- /dev/null
+++ b/drivers/net/mtk_eth/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 MediaTek Inc.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+
+obj-y += mtk_eth.o
+obj-$(CONFIG_MTK_ETH_SWITCH_MT7530) += mt753x.o mt7530.o
+obj-$(CONFIG_MTK_ETH_SWITCH_MT7531) += mt753x.o mt7531.o
+obj-$(CONFIG_MTK_ETH_SWITCH_MT7988) += mt753x.o mt7988.o
+obj-$(CONFIG_MTK_ETH_SWITCH_AN8855) += an8855.o
diff --git a/drivers/net/mtk_eth/an8855.c b/drivers/net/mtk_eth/an8855.c
new file mode 100644
index 00000000000..25a98e0f935
--- /dev/null
+++ b/drivers/net/mtk_eth/an8855.c
@@ -0,0 +1,1095 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 MediaTek Inc.
+ *
+ * Author: Neal Yen <neal.yen@mediatek.com>
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <phy.h>
+#include <miiphy.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include "mtk_eth.h"
+
+/* AN8855 Register Definitions */
+#define AN8855_SYS_CTRL_REG 0x100050c0
+#define AN8855_SW_SYS_RST BIT(31)
+
+#define AN8855_PMCR_REG(p) (0x10210000 + (p) * 0x200)
+#define AN8855_FORCE_MODE_LNK BIT(31)
+#define AN8855_FORCE_MODE 0xb31593f0
+
+#define AN8855_PORT_CTRL_BASE 0x10208000
+#define AN8855_PORT_CTRL_REG(p, r) (AN8855_PORT_CTRL_BASE + (p) * 0x200 + (r))
+
+#define AN8855_PORTMATRIX_REG(p) AN8855_PORT_CTRL_REG(p, 0x44)
+
+#define AN8855_PVC(p) AN8855_PORT_CTRL_REG(p, 0x10)
+#define AN8855_STAG_VPID_S 16
+#define AN8855_STAG_VPID_M 0xffff0000
+#define AN8855_VLAN_ATTR_S 6
+#define AN8855_VLAN_ATTR_M 0xc0
+
+#define VLAN_ATTR_USER 0
+
+#define AN8855_INT_MASK 0x100050F0
+#define AN8855_INT_SYS_BIT BIT(15)
+
+#define AN8855_RG_CLK_CPU_ICG 0x10005034
+#define AN8855_MCU_ENABLE BIT(3)
+
+#define AN8855_RG_TIMER_CTL 0x1000a100
+#define AN8855_WDOG_ENABLE BIT(25)
+
+#define AN8855_CKGCR 0x10213e1c
+
+#define AN8855_SCU_BASE 0x10000000
+#define AN8855_RG_RGMII_TXCK_C (AN8855_SCU_BASE + 0x1d0)
+#define AN8855_RG_GPIO_LED_MODE (AN8855_SCU_BASE + 0x0054)
+#define AN8855_RG_GPIO_LED_SEL(i) (AN8855_SCU_BASE + (0x0058 + ((i) * 4)))
+#define AN8855_RG_INTB_MODE (AN8855_SCU_BASE + 0x0080)
+#define AN8855_RG_GDMP_RAM (AN8855_SCU_BASE + 0x10000)
+#define AN8855_RG_GPIO_L_INV (AN8855_SCU_BASE + 0x0010)
+#define AN8855_RG_GPIO_CTRL (AN8855_SCU_BASE + 0xa300)
+#define AN8855_RG_GPIO_DATA (AN8855_SCU_BASE + 0xa304)
+#define AN8855_RG_GPIO_OE (AN8855_SCU_BASE + 0xa314)
+
+#define AN8855_HSGMII_AN_CSR_BASE 0x10220000
+#define AN8855_SGMII_REG_AN0 (AN8855_HSGMII_AN_CSR_BASE + 0x000)
+#define AN8855_SGMII_REG_AN_13 (AN8855_HSGMII_AN_CSR_BASE + 0x034)
+#define AN8855_SGMII_REG_AN_FORCE_CL37 (AN8855_HSGMII_AN_CSR_BASE + 0x060)
+
+#define AN8855_HSGMII_CSR_PCS_BASE 0x10220000
+#define AN8855_RG_HSGMII_PCS_CTROL_1 (AN8855_HSGMII_CSR_PCS_BASE + 0xa00)
+#define AN8855_RG_AN_SGMII_MODE_FORCE (AN8855_HSGMII_CSR_PCS_BASE + 0xa24)
+
+#define AN8855_MULTI_SGMII_CSR_BASE 0x10224000
+#define AN8855_SGMII_STS_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x018)
+#define AN8855_MSG_RX_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x100)
+#define AN8855_MSG_RX_LIK_STS_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x514)
+#define AN8855_MSG_RX_LIK_STS_2 (AN8855_MULTI_SGMII_CSR_BASE + 0x51c)
+#define AN8855_PHY_RX_FORCE_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x520)
+
+#define AN8855_XFI_CSR_PCS_BASE 0x10225000
+#define AN8855_RG_USXGMII_AN_CONTROL_0 (AN8855_XFI_CSR_PCS_BASE + 0xbf8)
+
+#define AN8855_MULTI_PHY_RA_CSR_BASE 0x10226000
+#define AN8855_RG_RATE_ADAPT_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x000)
+#define AN8855_RATE_ADP_P0_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x100)
+#define AN8855_MII_RA_AN_ENABLE (AN8855_MULTI_PHY_RA_CSR_BASE + 0x300)
+
+#define AN8855_QP_DIG_CSR_BASE 0x1022a000
+#define AN8855_QP_CK_RST_CTRL_4 (AN8855_QP_DIG_CSR_BASE + 0x310)
+#define AN8855_QP_DIG_MODE_CTRL_0 (AN8855_QP_DIG_CSR_BASE + 0x324)
+#define AN8855_QP_DIG_MODE_CTRL_1 (AN8855_QP_DIG_CSR_BASE + 0x330)
+
+#define AN8855_QP_PMA_TOP_BASE 0x1022e000
+#define AN8855_PON_RXFEDIG_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x100)
+#define AN8855_PON_RXFEDIG_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x124)
+
+#define AN8855_SS_LCPLL_PWCTL_SETTING_2 (AN8855_QP_PMA_TOP_BASE + 0x208)
+#define AN8855_SS_LCPLL_TDC_FLT_2 (AN8855_QP_PMA_TOP_BASE + 0x230)
+#define AN8855_SS_LCPLL_TDC_FLT_5 (AN8855_QP_PMA_TOP_BASE + 0x23c)
+#define AN8855_SS_LCPLL_TDC_PCW_1 (AN8855_QP_PMA_TOP_BASE + 0x248)
+#define AN8855_INTF_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x320)
+#define AN8855_INTF_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x324)
+#define AN8855_PLL_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x400)
+#define AN8855_PLL_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x408)
+#define AN8855_PLL_CTRL_3 (AN8855_QP_PMA_TOP_BASE + 0x40c)
+#define AN8855_PLL_CTRL_4 (AN8855_QP_PMA_TOP_BASE + 0x410)
+#define AN8855_PLL_CK_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x414)
+#define AN8855_RX_DLY_0 (AN8855_QP_PMA_TOP_BASE + 0x614)
+#define AN8855_RX_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x630)
+#define AN8855_RX_CTRL_5 (AN8855_QP_PMA_TOP_BASE + 0x63c)
+#define AN8855_RX_CTRL_6 (AN8855_QP_PMA_TOP_BASE + 0x640)
+#define AN8855_RX_CTRL_7 (AN8855_QP_PMA_TOP_BASE + 0x644)
+#define AN8855_RX_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x648)
+#define AN8855_RX_CTRL_26 (AN8855_QP_PMA_TOP_BASE + 0x690)
+#define AN8855_RX_CTRL_42 (AN8855_QP_PMA_TOP_BASE + 0x6d0)
+
+#define AN8855_QP_ANA_CSR_BASE 0x1022f000
+#define AN8855_RG_QP_RX_DAC_EN (AN8855_QP_ANA_CSR_BASE + 0x00)
+#define AN8855_RG_QP_RXAFE_RESERVE (AN8855_QP_ANA_CSR_BASE + 0x04)
+#define AN8855_RG_QP_CDR_LPF_MJV_LIM (AN8855_QP_ANA_CSR_BASE + 0x0c)
+#define AN8855_RG_QP_CDR_LPF_SETVALUE (AN8855_QP_ANA_CSR_BASE + 0x14)
+#define AN8855_RG_QP_CDR_PR_CKREF_DIV1 (AN8855_QP_ANA_CSR_BASE + 0x18)
+#define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE (AN8855_QP_ANA_CSR_BASE + 0x1c)
+#define AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF (AN8855_QP_ANA_CSR_BASE + 0x20)
+#define AN8855_RG_QP_TX_MODE_16B_EN (AN8855_QP_ANA_CSR_BASE + 0x28)
+#define AN8855_RG_QP_PLL_IPLL_DIG_PWR_SEL (AN8855_QP_ANA_CSR_BASE + 0x3c)
+#define AN8855_RG_QP_PLL_SDM_ORD (AN8855_QP_ANA_CSR_BASE + 0x40)
+
+#define AN8855_ETHER_SYS_BASE 0x1028c800
+#define RG_GPHY_AFE_PWD (AN8855_ETHER_SYS_BASE + 0x40)
+
+#define AN8855_PKG_SEL 0x10000094
+#define PAG_SEL_AN8855H 0x2
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT 0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2))
+
+#define PHY_PMA_CTRL 0x340
+
+#define PHY_DEV1F 0x1f
+
+#define PHY_LED_ON_CTRL(i) (0x24 + ((i) * 2))
+#define LED_ON_EN BIT(15)
+#define LED_ON_POL BIT(14)
+#define LED_ON_EVT_MASK 0x7f
+
+/* LED ON Event */
+#define LED_ON_EVT_FORCE BIT(6)
+#define LED_ON_EVT_LINK_HD BIT(5)
+#define LED_ON_EVT_LINK_FD BIT(4)
+#define LED_ON_EVT_LINK_DOWN BIT(3)
+#define LED_ON_EVT_LINK_10M BIT(2)
+#define LED_ON_EVT_LINK_100M BIT(1)
+#define LED_ON_EVT_LINK_1000M BIT(0)
+
+#define PHY_LED_BLK_CTRL(i) (0x25 + ((i) * 2))
+#define LED_BLK_EVT_MASK 0x3ff
+/* LED Blinking Event */
+#define LED_BLK_EVT_FORCE BIT(9)
+#define LED_BLK_EVT_10M_RX_ACT BIT(5)
+#define LED_BLK_EVT_10M_TX_ACT BIT(4)
+#define LED_BLK_EVT_100M_RX_ACT BIT(3)
+#define LED_BLK_EVT_100M_TX_ACT BIT(2)
+#define LED_BLK_EVT_1000M_RX_ACT BIT(1)
+#define LED_BLK_EVT_1000M_TX_ACT BIT(0)
+
+#define PHY_LED_BCR (0x21)
+#define LED_BCR_EXT_CTRL BIT(15)
+#define LED_BCR_CLK_EN BIT(3)
+#define LED_BCR_TIME_TEST BIT(2)
+#define LED_BCR_MODE_MASK 3
+#define LED_BCR_MODE_DISABLE 0
+
+#define PHY_LED_ON_DUR 0x22
+#define LED_ON_DUR_MASK 0xffff
+
+#define PHY_LED_BLK_DUR 0x23
+#define LED_BLK_DUR_MASK 0xffff
+
+#define PHY_LED_BLINK_DUR_CTRL 0x720
+
+/* Definition of LED */
+#define LED_ON_EVENT (LED_ON_EVT_LINK_1000M | \
+ LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\
+ LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD)
+
+#define LED_BLK_EVENT (LED_BLK_EVT_1000M_TX_ACT | \
+ LED_BLK_EVT_1000M_RX_ACT | \
+ LED_BLK_EVT_100M_TX_ACT | \
+ LED_BLK_EVT_100M_RX_ACT | \
+ LED_BLK_EVT_10M_TX_ACT | \
+ LED_BLK_EVT_10M_RX_ACT)
+
+#define LED_FREQ AIR_LED_BLK_DUR_64M
+
+#define AN8855_NUM_PHYS 5
+#define AN8855_NUM_PORTS 6
+#define AN8855_PHY_ADDR(base, addr) (((base) + (addr)) & 0x1f)
+
+/* PHY LED Register bitmap of define */
+#define PHY_LED_CTRL_SELECT 0x3e8
+#define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2))
+#define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2))
+#define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2))
+
+/* AN8855 LED */
+enum an8855_led_blk_dur {
+ AIR_LED_BLK_DUR_32M,
+ AIR_LED_BLK_DUR_64M,
+ AIR_LED_BLK_DUR_128M,
+ AIR_LED_BLK_DUR_256M,
+ AIR_LED_BLK_DUR_512M,
+ AIR_LED_BLK_DUR_1024M,
+ AIR_LED_BLK_DUR_LAST
+};
+
+enum an8855_led_polarity {
+ LED_LOW,
+ LED_HIGH,
+};
+
+enum an8855_led_mode {
+ AN8855_LED_MODE_DISABLE,
+ AN8855_LED_MODE_USER_DEFINE,
+ AN8855_LED_MODE_LAST
+};
+
+enum phy_led_idx {
+ P0_LED0,
+ P0_LED1,
+ P0_LED2,
+ P0_LED3,
+ P1_LED0,
+ P1_LED1,
+ P1_LED2,
+ P1_LED3,
+ P2_LED0,
+ P2_LED1,
+ P2_LED2,
+ P2_LED3,
+ P3_LED0,
+ P3_LED1,
+ P3_LED2,
+ P3_LED3,
+ P4_LED0,
+ P4_LED1,
+ P4_LED2,
+ P4_LED3,
+ PHY_LED_MAX
+};
+
+struct an8855_led_cfg {
+ u16 en;
+ u8 phy_led_idx;
+ u16 pol;
+ u16 on_cfg;
+ u16 blk_cfg;
+ u8 led_freq;
+};
+
+struct an8855_switch_priv {
+ struct mtk_eth_switch_priv epriv;
+ struct mii_dev *mdio_bus;
+ u32 phy_base;
+};
+
+/* AN8855 Reference Board */
+static const struct an8855_led_cfg led_cfg[] = {
+/*************************************************************************
+ * Enable, LED idx, LED Polarity, LED ON event, LED Blink event LED Freq
+ *************************************************************************
+ */
+ /* GPIO0 */
+ {1, P4_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO1 */
+ {1, P4_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO2 */
+ {1, P0_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO3 */
+ {1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO4 */
+ {1, P1_LED0, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO5 */
+ {1, P1_LED1, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO6 */
+ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO7 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO8 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO9 */
+ {1, P2_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO10 */
+ {1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO11 */
+ {1, P3_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO12 */
+ {1, P3_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO13 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO14 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO15 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO16 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO17 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO18 */
+ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO19 */
+ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+ /* GPIO20 */
+ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ},
+};
+
+static int __an8855_reg_read(struct mtk_eth_priv *priv, u8 phy_base, u32 reg, u32 *data)
+{
+ int ret, low_word, high_word;
+
+ ret = mtk_mii_write(priv, phy_base, 0x1f, 0x4);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv, phy_base, 0x10, 0);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv, phy_base, 0x15, ((reg >> 16) & 0xFFFF));
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv, phy_base, 0x16, (reg & 0xFFFF));
+ if (ret)
+ return ret;
+
+ low_word = mtk_mii_read(priv, phy_base, 0x18);
+ if (low_word < 0)
+ return low_word;
+
+ high_word = mtk_mii_read(priv, phy_base, 0x17);
+ if (high_word < 0)
+ return high_word;
+
+ ret = mtk_mii_write(priv, phy_base, 0x1f, 0);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv, phy_base, 0x10, 0);
+ if (ret)
+ return ret;
+
+ if (data)
+ *data = ((u32)high_word << 16) | (low_word & 0xffff);
+
+ return 0;
+}
+
+static int an8855_reg_read(struct an8855_switch_priv *priv, u32 reg, u32 *data)
+{
+ return __an8855_reg_read(priv->epriv.eth, priv->phy_base, reg, data);
+}
+
+static int an8855_reg_write(struct an8855_switch_priv *priv, u32 reg, u32 data)
+{
+ int ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0x4);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x11,
+ ((reg >> 16) & 0xFFFF));
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x12,
+ (reg & 0xFFFF));
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x13,
+ ((data >> 16) & 0xFFFF));
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x14,
+ (data & 0xFFFF));
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0);
+ if (ret)
+ return ret;
+
+ ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int an8855_phy_cl45_read(struct an8855_switch_priv *priv, int port,
+ int devad, int regnum, u16 *data)
+{
+ u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port);
+
+ *data = mtk_mmd_ind_read(priv->epriv.eth, phy_addr, devad, regnum);
+
+ return 0;
+}
+
+static int an8855_phy_cl45_write(struct an8855_switch_priv *priv, int port,
+ int devad, int regnum, u16 data)
+{
+ u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port);
+
+ mtk_mmd_ind_write(priv->epriv.eth, phy_addr, devad, regnum, data);
+
+ return 0;
+}
+
+static int an8855_port_sgmii_init(struct an8855_switch_priv *priv, u32 port)
+{
+ u32 val = 0;
+
+ if (port != 5) {
+ printf("an8855: port %d is not a SGMII port\n", port);
+ return -EINVAL;
+ }
+
+ /* PLL */
+ an8855_reg_read(priv, AN8855_QP_DIG_MODE_CTRL_1, &val);
+ val &= ~(0x3 << 2);
+ val |= (0x1 << 2);
+ an8855_reg_write(priv, AN8855_QP_DIG_MODE_CTRL_1, val);
+
+ /* PLL - LPF */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val &= ~(0x3 << 0);
+ val |= (0x1 << 0);
+ val &= ~(0x7 << 2);
+ val |= (0x5 << 2);
+ val &= ~GENMASK(7, 6);
+ val &= ~(0x7 << 8);
+ val |= (0x3 << 8);
+ val |= BIT(29);
+ val &= ~GENMASK(13, 12);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ /* PLL - ICO */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+ val |= BIT(2);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val &= ~BIT(14);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ /* PLL - CHP */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val &= ~(0xf << 16);
+ val |= (0x6 << 16);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ /* PLL - PFD */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val &= ~(0x3 << 20);
+ val |= (0x1 << 20);
+ val &= ~(0x3 << 24);
+ val |= (0x1 << 24);
+ val &= ~BIT(26);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ /* PLL - POSTDIV */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val |= BIT(22);
+ val &= ~BIT(27);
+ val &= ~BIT(28);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ /* PLL - SDM */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+ val &= ~GENMASK(4, 3);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val);
+ val &= ~BIT(30);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_2, val);
+
+ an8855_reg_read(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, &val);
+ val &= ~(0x3 << 16);
+ val |= (0x1 << 16);
+ an8855_reg_write(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, val);
+
+ an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_2, 0x7a000000);
+ an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_PCW_1, 0x7a000000);
+
+ an8855_reg_read(priv, AN8855_SS_LCPLL_TDC_FLT_5, &val);
+ val &= ~BIT(24);
+ an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_5, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val);
+ val &= ~BIT(8);
+ an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val);
+
+ /* PLL - SS */
+ an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val);
+ val &= ~GENMASK(15, 0);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_3, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val);
+ val &= ~GENMASK(1, 0);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_4, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val);
+ val &= ~GENMASK(31, 16);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_3, val);
+
+ /* PLL - TDC */
+ an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val);
+ val &= ~BIT(9);
+ an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_PLL_SDM_ORD, &val);
+ val |= BIT(3);
+ val |= BIT(4);
+ an8855_reg_write(priv, AN8855_RG_QP_PLL_SDM_ORD, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_RX_DAC_EN, &val);
+ val &= ~(0x3 << 16);
+ val |= (0x2 << 16);
+ an8855_reg_write(priv, AN8855_RG_QP_RX_DAC_EN, val);
+
+ /* TCL Disable (only for Co-SIM) */
+ an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_0, &val);
+ val &= ~BIT(12);
+ an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_0, val);
+
+ /* TX Init */
+ an8855_reg_read(priv, AN8855_RG_QP_TX_MODE_16B_EN, &val);
+ val &= ~BIT(0);
+ val &= ~(0xffff << 16);
+ val |= (0x4 << 16);
+ an8855_reg_write(priv, AN8855_RG_QP_TX_MODE_16B_EN, val);
+
+ /* RX Control */
+ an8855_reg_read(priv, AN8855_RG_QP_RXAFE_RESERVE, &val);
+ val |= BIT(11);
+ an8855_reg_write(priv, AN8855_RG_QP_RXAFE_RESERVE, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, &val);
+ val &= ~(0x3 << 4);
+ val |= (0x1 << 4);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, &val);
+ val &= ~(0xf << 25);
+ val |= (0x1 << 25);
+ val &= ~(0x7 << 29);
+ val |= (0x3 << 29);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val);
+ val &= ~(0x1f << 8);
+ val |= (0xf << 8);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val);
+ val &= ~(0x3f << 0);
+ val |= (0x19 << 0);
+ val &= ~BIT(6);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, &val);
+ val &= ~(0x7f << 6);
+ val |= (0x21 << 6);
+ val &= ~(0x3 << 16);
+ val |= (0x2 << 16);
+ val &= ~BIT(13);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val);
+ val &= ~BIT(30);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
+
+ an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val);
+ val &= ~(0x7 << 24);
+ val |= (0x4 << 24);
+ an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val);
+
+ an8855_reg_read(priv, AN8855_PLL_CTRL_0, &val);
+ val |= BIT(0);
+ an8855_reg_write(priv, AN8855_PLL_CTRL_0, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_26, &val);
+ val &= ~BIT(23);
+ val |= BIT(26);
+ an8855_reg_write(priv, AN8855_RX_CTRL_26, val);
+
+ an8855_reg_read(priv, AN8855_RX_DLY_0, &val);
+ val &= ~(0xff << 0);
+ val |= (0x6f << 0);
+ val |= GENMASK(13, 8);
+ an8855_reg_write(priv, AN8855_RX_DLY_0, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_42, &val);
+ val &= ~(0x1fff << 0);
+ val |= (0x150 << 0);
+ an8855_reg_write(priv, AN8855_RX_CTRL_42, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_2, &val);
+ val &= ~(0x1fff << 16);
+ val |= (0x150 << 16);
+ an8855_reg_write(priv, AN8855_RX_CTRL_2, val);
+
+ an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_9, &val);
+ val &= ~(0x7 << 0);
+ val |= (0x1 << 0);
+ an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_9, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_8, &val);
+ val &= ~(0xfff << 16);
+ val |= (0x200 << 16);
+ val &= ~(0x7fff << 14);
+ val |= (0xfff << 14);
+ an8855_reg_write(priv, AN8855_RX_CTRL_8, val);
+
+ /* Frequency memter */
+ an8855_reg_read(priv, AN8855_RX_CTRL_5, &val);
+ val &= ~(0xfffff << 10);
+ val |= (0x10 << 10);
+ an8855_reg_write(priv, AN8855_RX_CTRL_5, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_6, &val);
+ val &= ~(0xfffff << 0);
+ val |= (0x64 << 0);
+ an8855_reg_write(priv, AN8855_RX_CTRL_6, val);
+
+ an8855_reg_read(priv, AN8855_RX_CTRL_7, &val);
+ val &= ~(0xfffff << 0);
+ val |= (0x2710 << 0);
+ an8855_reg_write(priv, AN8855_RX_CTRL_7, val);
+
+ /* PCS Init */
+ an8855_reg_read(priv, AN8855_RG_HSGMII_PCS_CTROL_1, &val);
+ val &= ~BIT(30);
+ an8855_reg_write(priv, AN8855_RG_HSGMII_PCS_CTROL_1, val);
+
+ /* Rate Adaption */
+ an8855_reg_read(priv, AN8855_RATE_ADP_P0_CTRL_0, &val);
+ val &= ~BIT(31);
+ an8855_reg_write(priv, AN8855_RATE_ADP_P0_CTRL_0, val);
+
+ an8855_reg_read(priv, AN8855_RG_RATE_ADAPT_CTRL_0, &val);
+ val |= BIT(0);
+ val |= BIT(4);
+ val |= GENMASK(27, 26);
+ an8855_reg_write(priv, AN8855_RG_RATE_ADAPT_CTRL_0, val);
+
+ /* Disable AN */
+ an8855_reg_read(priv, AN8855_SGMII_REG_AN0, &val);
+ val &= ~BIT(12);
+ an8855_reg_write(priv, AN8855_SGMII_REG_AN0, val);
+
+ /* Force Speed */
+ an8855_reg_read(priv, AN8855_SGMII_STS_CTRL_0, &val);
+ val |= BIT(2);
+ val |= GENMASK(5, 4);
+ an8855_reg_write(priv, AN8855_SGMII_STS_CTRL_0, val);
+
+ /* bypass flow control to MAC */
+ an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_0, 0x01010107);
+ an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_2, 0x00000EEF);
+
+ return 0;
+}
+
+static void an8855_led_set_usr_def(struct an8855_switch_priv *priv, u8 entity,
+ enum an8855_led_polarity pol, u16 on_evt,
+ u16 blk_evt, u8 led_freq)
+{
+ u32 cl45_data;
+
+ if (pol == LED_HIGH)
+ on_evt |= LED_ON_POL;
+ else
+ on_evt &= ~LED_ON_POL;
+
+ /* LED on event */
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_ON_CTRL(entity % 4),
+ on_evt | LED_ON_EN);
+
+ /* LED blink event */
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_BLK_CTRL(entity % 4),
+ blk_evt);
+
+ /* LED freq */
+ switch (led_freq) {
+ case AIR_LED_BLK_DUR_32M:
+ cl45_data = 0x30e;
+ break;
+
+ case AIR_LED_BLK_DUR_64M:
+ cl45_data = 0x61a;
+ break;
+
+ case AIR_LED_BLK_DUR_128M:
+ cl45_data = 0xc35;
+ break;
+
+ case AIR_LED_BLK_DUR_256M:
+ cl45_data = 0x186a;
+ break;
+
+ case AIR_LED_BLK_DUR_512M:
+ cl45_data = 0x30d4;
+ break;
+
+ case AIR_LED_BLK_DUR_1024M:
+ cl45_data = 0x61a8;
+ break;
+
+ default:
+ cl45_data = 0;
+ break;
+ }
+
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_BLK_DUR(entity % 4),
+ cl45_data);
+
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_ON_DUR(entity % 4),
+ (cl45_data >> 1));
+
+ /* Disable DATA & BAD_SSD for port LED blink behavior */
+ cl45_data = mtk_mmd_ind_read(priv->epriv.eth, (entity / 4), 0x1e, PHY_PMA_CTRL);
+ cl45_data &= ~BIT(0);
+ cl45_data &= ~BIT(15);
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_PMA_CTRL, cl45_data);
+}
+
+static int an8855_led_set_mode(struct an8855_switch_priv *priv, u8 mode)
+{
+ u16 cl45_data;
+
+ an8855_phy_cl45_read(priv, 0, 0x1f, PHY_LED_BCR, &cl45_data);
+
+ switch (mode) {
+ case AN8855_LED_MODE_DISABLE:
+ cl45_data &= ~LED_BCR_EXT_CTRL;
+ cl45_data &= ~LED_BCR_MODE_MASK;
+ cl45_data |= LED_BCR_MODE_DISABLE;
+ break;
+
+ case AN8855_LED_MODE_USER_DEFINE:
+ cl45_data |= LED_BCR_EXT_CTRL;
+ cl45_data |= LED_BCR_CLK_EN;
+ break;
+
+ default:
+ printf("an8855: LED mode%d is not supported!\n", mode);
+ return -EINVAL;
+ }
+
+ an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BCR, cl45_data);
+
+ return 0;
+}
+
+static int an8855_led_set_state(struct an8855_switch_priv *priv, u8 entity,
+ u8 state)
+{
+ u16 cl45_data = 0;
+
+ /* Change to per port contorl */
+ an8855_phy_cl45_read(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT,
+ &cl45_data);
+
+ if (state == 1)
+ cl45_data |= (1 << (entity % 4));
+ else
+ cl45_data &= ~(1 << (entity % 4));
+
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT,
+ cl45_data);
+
+ /* LED enable setting */
+ an8855_phy_cl45_read(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_ON_CTRL(entity % 4), &cl45_data);
+
+ if (state == 1)
+ cl45_data |= LED_ON_EN;
+ else
+ cl45_data &= ~LED_ON_EN;
+
+ an8855_phy_cl45_write(priv, (entity / 4), 0x1e,
+ PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data);
+
+ return 0;
+}
+
+static int an8855_led_init(struct an8855_switch_priv *priv)
+{
+ u32 val, id, tmp_id = 0;
+ int ret;
+
+ ret = an8855_led_set_mode(priv, AN8855_LED_MODE_USER_DEFINE);
+ if (ret) {
+ printf("an8855: led_set_mode failed with %d!\n", ret);
+ return ret;
+ }
+
+ for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+ ret = an8855_led_set_state(priv, led_cfg[id].phy_led_idx,
+ led_cfg[id].en);
+ if (ret != 0) {
+ printf("an8855: led_set_state failed with %d!\n", ret);
+ return ret;
+ }
+
+ if (led_cfg[id].en == 1) {
+ an8855_led_set_usr_def(priv,
+ led_cfg[id].phy_led_idx,
+ led_cfg[id].pol,
+ led_cfg[id].on_cfg,
+ led_cfg[id].blk_cfg,
+ led_cfg[id].led_freq);
+ }
+ }
+
+ /* Setting for System LED & Loop LED */
+ an8855_reg_write(priv, AN8855_RG_GPIO_OE, 0x0);
+ an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x0);
+ an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, 0);
+
+ an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x1001);
+ an8855_reg_read(priv, AN8855_RG_GPIO_DATA, &val);
+ val |= GENMASK(3, 1);
+ val &= ~(BIT(0));
+ val &= ~(BIT(6));
+ an8855_reg_write(priv, AN8855_RG_GPIO_DATA, val);
+
+ an8855_reg_read(priv, AN8855_RG_GPIO_OE, &val);
+ val |= 0x41;
+ an8855_reg_write(priv, AN8855_RG_GPIO_OE, val);
+
+ /* Mapping between GPIO & LED */
+ val = 0;
+ for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+ /* Skip GPIO6, due to GPIO6 does not support PORT LED */
+ if (id == 6)
+ continue;
+
+ if (led_cfg[id].en == 1) {
+ if (id < 7)
+ val |= led_cfg[id].phy_led_idx << ((id % 4) * 8);
+ else
+ val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8);
+ }
+
+ if (id < 7)
+ tmp_id = id;
+ else
+ tmp_id = id - 1;
+
+ if ((tmp_id % 4) == 0x3) {
+ an8855_reg_write(priv,
+ AN8855_RG_GPIO_LED_SEL(tmp_id / 4),
+ val);
+ val = 0;
+ }
+ }
+
+ /* Turn on LAN LED mode */
+ val = 0;
+ for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+ if (led_cfg[id].en == 1)
+ val |= 0x1 << id;
+ }
+ an8855_reg_write(priv, AN8855_RG_GPIO_LED_MODE, val);
+
+ /* Force clear blink pulse for per port LED */
+ an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0x1f);
+ udelay(1000);
+ an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0);
+
+ return 0;
+}
+
+static void an8855_port_isolation(struct an8855_switch_priv *priv)
+{
+ u32 i;
+
+ for (i = 0; i < AN8855_NUM_PORTS; i++) {
+ /* Set port matrix mode */
+ if (i != 5)
+ an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x20);
+ else
+ an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x1f);
+
+ /* Set port mode to user port */
+ an8855_reg_write(priv, AN8855_PVC(i),
+ (0x8100 << AN8855_STAG_VPID_S) |
+ (VLAN_ATTR_USER << AN8855_VLAN_ATTR_S));
+ }
+}
+
+static void an8855_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
+{
+ struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+ u32 pmcr = AN8855_FORCE_MODE_LNK;
+
+ if (enable)
+ pmcr = AN8855_FORCE_MODE;
+
+ an8855_reg_write(priv, AN8855_PMCR_REG(5), pmcr);
+}
+
+static int an8855_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ struct an8855_switch_priv *priv = bus->priv;
+
+ if (devad < 0)
+ return mtk_mii_read(priv->epriv.eth, addr, reg);
+
+ return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg);
+}
+
+static int an8855_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
+ u16 val)
+{
+ struct an8855_switch_priv *priv = bus->priv;
+
+ if (devad < 0)
+ return mtk_mii_write(priv->epriv.eth, addr, reg, val);
+
+ return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val);
+}
+
+static int an8855_mdio_register(struct an8855_switch_priv *priv)
+{
+ struct mii_dev *mdio_bus = mdio_alloc();
+ int ret;
+
+ if (!mdio_bus)
+ return -ENOMEM;
+
+ mdio_bus->read = an8855_mdio_read;
+ mdio_bus->write = an8855_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;
+}
+
+static int an8855_setup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+ u16 phy_addr, phy_val;
+ u32 i, id, val = 0;
+ int ret;
+
+ priv->phy_base = 1;
+
+ /* Turn off PHYs */
+ for (i = 0; i < AN8855_NUM_PHYS; i++) {
+ phy_addr = AN8855_PHY_ADDR(priv->phy_base, i);
+ phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+ phy_val |= BMCR_PDOWN;
+ mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+ }
+
+ /* Force MAC link down before reset */
+ an8855_reg_write(priv, AN8855_PMCR_REG(5), AN8855_FORCE_MODE_LNK);
+
+ /* Switch soft reset */
+ an8855_reg_write(priv, AN8855_SYS_CTRL_REG, AN8855_SW_SYS_RST);
+ mdelay(100);
+
+ an8855_reg_read(priv, AN8855_PKG_SEL, &val);
+ if ((val & 0x7) == PAG_SEL_AN8855H) {
+ /* Release power down */
+ an8855_reg_write(priv, RG_GPHY_AFE_PWD, 0x0);
+
+ /* Invert for LED activity change */
+ an8855_reg_read(priv, AN8855_RG_GPIO_L_INV, &val);
+ for (id = 0; id < ARRAY_SIZE(led_cfg); id++) {
+ if (led_cfg[id].pol == LED_HIGH && led_cfg[id].en == 1)
+ val |= 0x1 << id;
+ }
+ an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, (val | 0x1));
+
+ /* MCU NOP CMD */
+ an8855_reg_write(priv, AN8855_RG_GDMP_RAM, 0x846);
+ an8855_reg_write(priv, AN8855_RG_GDMP_RAM + 4, 0x4a);
+
+ /* Enable MCU */
+ an8855_reg_read(priv, AN8855_RG_CLK_CPU_ICG, &val);
+ an8855_reg_write(priv, AN8855_RG_CLK_CPU_ICG,
+ val | AN8855_MCU_ENABLE);
+ udelay(1000);
+
+ /* Disable MCU watchdog */
+ an8855_reg_read(priv, AN8855_RG_TIMER_CTL, &val);
+ an8855_reg_write(priv, AN8855_RG_TIMER_CTL,
+ (val & (~AN8855_WDOG_ENABLE)));
+
+ /* LED settings for T830 reference board */
+ ret = an8855_led_init(priv);
+ if (ret < 0) {
+ printf("an8855: an8855_led_init failed with %d\n", ret);
+ return ret;
+ }
+ }
+
+ switch (priv->epriv.phy_interface) {
+ case PHY_INTERFACE_MODE_2500BASEX:
+ an8855_port_sgmii_init(priv, 5);
+ break;
+
+ default:
+ break;
+ }
+
+ an8855_reg_read(priv, AN8855_CKGCR, &val);
+ val &= ~(0x3);
+ an8855_reg_write(priv, AN8855_CKGCR, val);
+
+ /* Enable port isolation to block inter-port communication */
+ an8855_port_isolation(priv);
+
+ /* Turn on PHYs */
+ for (i = 0; i < AN8855_NUM_PHYS; i++) {
+ phy_addr = AN8855_PHY_ADDR(priv->phy_base, i);
+ phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+ phy_val &= ~BMCR_PDOWN;
+ mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+ }
+
+ return an8855_mdio_register(priv);
+}
+
+static int an8855_cleanup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv;
+
+ mdio_unregister(priv->mdio_bus);
+
+ return 0;
+}
+
+static int an8855_detect(struct mtk_eth_priv *priv)
+{
+ int ret;
+ u32 val;
+
+ ret = __an8855_reg_read(priv, 1, 0x10005000, &val);
+ if (ret)
+ return ret;
+
+ if (val == 0x8855)
+ return 0;
+
+ return -ENODEV;
+}
+
+MTK_ETH_SWITCH(an8855) = {
+ .name = "an8855",
+ .desc = "Airoha AN8855",
+ .priv_size = sizeof(struct an8855_switch_priv),
+ .reset_wait_time = 100,
+
+ .detect = an8855_detect,
+ .setup = an8855_setup,
+ .cleanup = an8855_cleanup,
+ .mac_control = an8855_mac_control,
+};
diff --git a/drivers/net/mtk_eth/mt7530.c b/drivers/net/mtk_eth/mt7530.c
new file mode 100644
index 00000000000..3065d35e126
--- /dev/null
+++ b/drivers/net/mtk_eth/mt7530.c
@@ -0,0 +1,281 @@
+// 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 <miiphy.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include "mtk_eth.h"
+#include "mt753x.h"
+
+#define CHIP_REV 0x7ffc
+#define CHIP_NAME_S 16
+#define CHIP_NAME_M 0xffff0000
+#define CHIP_REV_S 0
+#define CHIP_REV_M 0x0f
+
+static void mt7530_core_reg_write(struct mt753x_switch_priv *priv, u32 reg,
+ u32 val)
+{
+ u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0);
+
+ mtk_mmd_ind_write(priv->epriv.eth, phy_addr, 0x1f, reg, val);
+}
+
+static int mt7530_pad_clk_setup(struct mt753x_switch_priv *priv, int mode)
+{
+ u32 ncpo1, ssc_delta;
+
+ switch (mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ ncpo1 = 0x0c80;
+ ssc_delta = 0x87;
+ break;
+
+ default:
+ printf("error: xMII mode %d is not supported\n", mode);
+ return -EINVAL;
+ }
+
+ /* Disable MT7530 core clock */
+ mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0);
+
+ /* Disable MT7530 PLL */
+ mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1,
+ (2 << RG_GSWPLL_POSDIV_200M_S) |
+ (32 << RG_GSWPLL_FBKDIV_200M_S));
+
+ /* For MT7530 core clock = 500Mhz */
+ mt7530_core_reg_write(priv, CORE_GSWPLL_GRP2,
+ (1 << RG_GSWPLL_POSDIV_500M_S) |
+ (25 << RG_GSWPLL_FBKDIV_500M_S));
+
+ /* Enable MT7530 PLL */
+ mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1,
+ (2 << RG_GSWPLL_POSDIV_200M_S) |
+ (32 << RG_GSWPLL_FBKDIV_200M_S) |
+ RG_GSWPLL_EN_PRE);
+
+ udelay(20);
+
+ mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+
+ /* Setup the MT7530 TRGMII Tx Clock */
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1);
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP6, 0);
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta);
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta);
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
+ RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN);
+
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP2,
+ RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
+ (1 << RG_SYSPLL_POSDIV_S));
+
+ mt7530_core_reg_write(priv, CORE_PLL_GROUP7,
+ RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) |
+ RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
+
+ /* Enable MT7530 core clock */
+ mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG,
+ REG_GSWCK_EN | REG_TRGMIICK_EN);
+
+ return 0;
+}
+
+static void mt7530_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u32 pmcr = FORCE_MODE;
+
+ if (enable)
+ pmcr = priv->pmcr;
+
+ mt753x_reg_write(priv, PMCR_REG(6), pmcr);
+}
+
+static int mt7530_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ struct mt753x_switch_priv *priv = bus->priv;
+
+ if (devad < 0)
+ return mtk_mii_read(priv->epriv.eth, addr, reg);
+
+ return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg);
+}
+
+static int mt7530_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 mtk_mii_write(priv->epriv.eth, addr, reg, val);
+
+ return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val);
+}
+
+static int mt7530_mdio_register(struct mt753x_switch_priv *priv)
+{
+ struct mii_dev *mdio_bus = mdio_alloc();
+ int ret;
+
+ if (!mdio_bus)
+ return -ENOMEM;
+
+ mdio_bus->read = mt7530_mdio_read;
+ mdio_bus->write = mt7530_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;
+}
+
+static int mt7530_setup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u16 phy_addr, phy_val;
+ u32 i, val, txdrv;
+
+ priv->smi_addr = MT753X_DFL_SMI_ADDR;
+ priv->reg_read = mt753x_mdio_reg_read;
+ priv->reg_write = mt753x_mdio_reg_write;
+
+ if (!MTK_HAS_CAPS(priv->epriv.soc->caps, MTK_TRGMII_MT7621_CLK)) {
+ /* Select 250MHz clk for RGMII mode */
+ mtk_ethsys_rmw(priv->epriv.eth, ETHSYS_CLKCFG0_REG,
+ ETHSYS_TRGMII_CLK_SEL362_5, 0);
+
+ txdrv = 8;
+ } else {
+ txdrv = 4;
+ }
+
+ /* Modify HWTRAP first to allow direct access to internal PHYs */
+ mt753x_reg_read(priv, HWTRAP_REG, &val);
+ val |= CHG_TRAP;
+ val &= ~C_MDIO_BPS;
+ mt753x_reg_write(priv, MHWTRAP_REG, val);
+
+ /* Calculate the phy base address */
+ val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3;
+ priv->phy_base = (val | 0x7) + 1;
+
+ /* Turn off PHYs */
+ for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+ phy_val |= BMCR_PDOWN;
+ mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+ }
+
+ /* Force MAC link down before reset */
+ mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
+ mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE);
+
+ /* MT7530 reset */
+ mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
+ udelay(100);
+
+ val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+ MAC_MODE | FORCE_MODE |
+ MAC_TX_EN | MAC_RX_EN |
+ BKOFF_EN | BACKPR_EN |
+ (SPEED_1000M << FORCE_SPD_S) |
+ FORCE_DPX | FORCE_LINK;
+
+ /* MT7530 Port6: Forced 1000M/FD, FC disabled */
+ priv->pmcr = val;
+
+ /* MT7530 Port5: Forced link down */
+ mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
+
+ /* Keep MAC link down before starting eth */
+ mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE);
+
+ /* MT7530 Port6: Set to RGMII */
+ mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII);
+
+ /* Hardware Trap: Enable Port6, Disable Port5 */
+ mt753x_reg_read(priv, HWTRAP_REG, &val);
+ val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS |
+ (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) |
+ (P5_INTF_MODE_RGMII << P5_INTF_MODE_S);
+ val &= ~(C_MDIO_BPS | P6_INTF_DIS);
+ mt753x_reg_write(priv, MHWTRAP_REG, val);
+
+ /* Setup switch core pll */
+ mt7530_pad_clk_setup(priv, priv->epriv.phy_interface);
+
+ /* Lower Tx Driving for TRGMII path */
+ for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
+ mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
+ (txdrv << TD_DM_DRVP_S) |
+ (txdrv << TD_DM_DRVN_S));
+
+ for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+ mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
+
+ /* Enable port isolation to block inter-port communication */
+ mt753x_port_isolation(priv);
+
+ /* Turn on PHYs */
+ for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR);
+ phy_val &= ~BMCR_PDOWN;
+ mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val);
+ }
+
+ return mt7530_mdio_register(priv);
+}
+
+static int mt7530_cleanup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+
+ mdio_unregister(priv->mdio_bus);
+
+ return 0;
+}
+
+static int mt7530_detect(struct mtk_eth_priv *priv)
+{
+ int ret;
+ u32 rev;
+
+ ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev);
+ if (ret)
+ return ret;
+
+ if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7530)
+ return 0;
+
+ return -ENODEV;
+}
+
+MTK_ETH_SWITCH(mt7530) = {
+ .name = "mt7530",
+ .desc = "MediaTek MT7530",
+ .priv_size = sizeof(struct mt753x_switch_priv),
+ .reset_wait_time = 1000,
+
+ .detect = mt7530_detect,
+ .setup = mt7530_setup,
+ .cleanup = mt7530_cleanup,
+ .mac_control = mt7530_mac_control,
+};
diff --git a/drivers/net/mtk_eth/mt7531.c b/drivers/net/mtk_eth/mt7531.c
new file mode 100644
index 00000000000..32d6bebbbdb
--- /dev/null
+++ b/drivers/net/mtk_eth/mt7531.c
@@ -0,0 +1,293 @@
+// 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 <miiphy.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include "mtk_eth.h"
+#include "mt753x.h"
+
+#define CHIP_REV 0x781C
+#define CHIP_NAME_S 16
+#define CHIP_NAME_M 0xffff0000
+#define CHIP_REV_S 0
+#define CHIP_REV_M 0x0f
+#define CHIP_REV_E1 0x0
+
+static int mt7531_core_reg_read(struct mt753x_switch_priv *priv, u32 reg)
+{
+ u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0);
+
+ return mt7531_mmd_read(priv, phy_addr, 0x1f, reg);
+}
+
+static void mt7531_core_reg_write(struct mt753x_switch_priv *priv, u32 reg,
+ u32 val)
+{
+ u8 phy_addr = MT753X_PHY_ADDR(priv->phy_base, 0);
+
+ mt7531_mmd_write(priv, phy_addr, 0x1f, reg, val);
+}
+
+static void mt7531_core_pll_setup(struct mt753x_switch_priv *priv)
+{
+ /* Step 1 : Disable MT7531 COREPLL */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0);
+
+ /* Step 2: switch to XTAL output */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW);
+
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0);
+
+ /* Step 3: disable PLLGP and enable program PLLGP */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP);
+
+ /* Step 4: program COREPLL output frequency to 500MHz */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M,
+ 2 << RG_COREPLL_POSDIV_S);
+ udelay(25);
+
+ /* Currently, support XTAL 25Mhz only */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M,
+ 0x140000 << RG_COREPLL_SDM_PCW_S);
+
+ /* Set feedback divide ratio update signal to high */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG,
+ RG_COREPLL_SDM_PCW_CHG);
+
+ /* Wait for at least 16 XTAL clocks */
+ udelay(10);
+
+ /* Step 5: set feedback divide ratio update signal to low */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0);
+
+ /* add enable 325M clock for SGMII */
+ mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000);
+
+ /* add enable 250SSC clock for RGMII */
+ mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000);
+
+ /*Step 6: Enable MT7531 PLL */
+ mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN);
+
+ mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL);
+
+ udelay(25);
+}
+
+static int mt7531_port_sgmii_init(struct mt753x_switch_priv *priv, u32 port)
+{
+ if (port != 5 && port != 6) {
+ printf("mt7531: port %d is not a SGMII port\n", port);
+ return -EINVAL;
+ }
+
+ /* Set SGMII GEN2 speed(2.5G) */
+ mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK,
+ FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500));
+
+ /* Disable SGMII AN */
+ mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port),
+ SGMII_AN_ENABLE, 0);
+
+ /* SGMII force mode setting */
+ mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE);
+
+ /* Release PHYA power down state */
+ mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
+ SGMII_PHYA_PWD, 0);
+
+ return 0;
+}
+
+static int mt7531_port_rgmii_init(struct mt753x_switch_priv *priv, u32 port)
+{
+ u32 val;
+
+ if (port != 5) {
+ printf("error: RGMII mode is not available for port %d\n",
+ port);
+ return -EINVAL;
+ }
+
+ mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val);
+ val |= GP_CLK_EN;
+ val &= ~GP_MODE_M;
+ val |= GP_MODE_RGMII << GP_MODE_S;
+ val |= TXCLK_NO_REVERSE;
+ val |= RXCLK_NO_DELAY;
+ val &= ~CLK_SKEW_IN_M;
+ val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S;
+ val &= ~CLK_SKEW_OUT_M;
+ val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S;
+ mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val);
+
+ return 0;
+}
+
+static void mt7531_phy_setting(struct mt753x_switch_priv *priv)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ /* Enable HW auto downshift */
+ mt7531_mii_write(priv, i, 0x1f, 0x1);
+ val = mt7531_mii_read(priv, i, PHY_EXT_REG_14);
+ val |= PHY_EN_DOWN_SHFIT;
+ mt7531_mii_write(priv, i, PHY_EXT_REG_14, val);
+
+ /* PHY link down power saving enable */
+ val = mt7531_mii_read(priv, i, PHY_EXT_REG_17);
+ val |= PHY_LINKDOWN_POWER_SAVING_EN;
+ mt7531_mii_write(priv, i, PHY_EXT_REG_17, val);
+
+ val = mt7531_mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6);
+ val &= ~PHY_POWER_SAVING_M;
+ val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S;
+ mt7531_mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val);
+ }
+}
+
+static void mt7531_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u32 pmcr = FORCE_MODE_LNK;
+
+ if (enable)
+ pmcr = priv->pmcr;
+
+ mt753x_reg_write(priv, PMCR_REG(5), pmcr);
+ mt753x_reg_write(priv, PMCR_REG(6), pmcr);
+}
+
+static int mt7531_setup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u32 i, val, pmcr, port5_sgmii;
+ u16 phy_addr, phy_val;
+
+ priv->smi_addr = MT753X_DFL_SMI_ADDR;
+ priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK;
+ priv->reg_read = mt753x_mdio_reg_read;
+ priv->reg_write = mt753x_mdio_reg_write;
+
+ /* 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 |= BMCR_PDOWN;
+ mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+ }
+
+ /* Force MAC link down before reset */
+ mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK);
+ mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
+
+ /* Switch soft reset */
+ mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
+ udelay(100);
+
+ /* Enable MDC input Schmitt Trigger */
+ mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN,
+ SMT_IOLB_5_SMI_MDC_EN);
+
+ mt7531_core_pll_setup(priv);
+
+ mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val);
+ port5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
+
+ /* port5 support either RGMII or SGMII, port6 only support SGMII. */
+ switch (priv->epriv.phy_interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ if (!port5_sgmii)
+ mt7531_port_rgmii_init(priv, 5);
+ break;
+
+ case PHY_INTERFACE_MODE_2500BASEX:
+ mt7531_port_sgmii_init(priv, 6);
+ if (port5_sgmii)
+ mt7531_port_sgmii_init(priv, 5);
+ break;
+
+ default:
+ break;
+ }
+
+ pmcr = MT7531_FORCE_MODE |
+ (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+ MAC_MODE | MAC_TX_EN | MAC_RX_EN |
+ BKOFF_EN | BACKPR_EN |
+ FORCE_RX_FC | FORCE_TX_FC |
+ (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX |
+ FORCE_LINK;
+
+ priv->pmcr = pmcr;
+
+ /* Keep MAC link down before starting eth */
+ mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK);
+ mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
+
+ /* Enable port isolation to block inter-port communication */
+ mt753x_port_isolation(priv);
+
+ /* 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 &= ~BMCR_PDOWN;
+ mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+ }
+
+ mt7531_phy_setting(priv);
+
+ /* Enable Internal PHYs */
+ val = mt7531_core_reg_read(priv, CORE_PLL_GROUP4);
+ val |= MT7531_BYPASS_MODE;
+ val &= ~MT7531_POWER_ON_OFF;
+ mt7531_core_reg_write(priv, CORE_PLL_GROUP4, val);
+
+ return mt7531_mdio_register(priv);
+}
+
+static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+
+ mdio_unregister(priv->mdio_bus);
+
+ return 0;
+}
+
+static int mt7531_detect(struct mtk_eth_priv *priv)
+{
+ int ret;
+ u32 rev;
+
+ ret = __mt753x_mdio_reg_read(priv, MT753X_DFL_SMI_ADDR, CHIP_REV, &rev);
+ if (ret)
+ return ret;
+
+ if (((rev & CHIP_NAME_M) >> CHIP_NAME_S) == 0x7531)
+ return 0;
+
+ return -ENODEV;
+}
+
+MTK_ETH_SWITCH(mt7531) = {
+ .name = "mt7531",
+ .desc = "MediaTek MT7531",
+ .priv_size = sizeof(struct mt753x_switch_priv),
+ .reset_wait_time = 200,
+
+ .detect = mt7531_detect,
+ .setup = mt7531_setup,
+ .cleanup = mt7531_cleanup,
+ .mac_control = mt7531_mac_control,
+};
diff --git a/drivers/net/mtk_eth/mt753x.c b/drivers/net/mtk_eth/mt753x.c
new file mode 100644
index 00000000000..cdd52f3ff1b
--- /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));
+ }
+}
diff --git a/drivers/net/mtk_eth/mt753x.h b/drivers/net/mtk_eth/mt753x.h
new file mode 100644
index 00000000000..65046a1421c
--- /dev/null
+++ b/drivers/net/mtk_eth/mt753x.h
@@ -0,0 +1,286 @@
+/* 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>
+ */
+
+#ifndef _MTK_ETH_MT753X_H_
+#define _MTK_ETH_MT753X_H_
+
+#include <phy.h>
+#include <miiphy.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+struct mtk_eth_priv;
+
+#define MT753X_NUM_PHYS 5
+#define MT753X_NUM_PORTS 7
+#define MT753X_DFL_SMI_ADDR 31
+#define MT753X_SMI_ADDR_MASK 0x1f
+
+#define MT753X_PHY_ADDR(base, addr) \
+ (((base) + (addr)) & 0x1f)
+
+/* MT7530 Registers */
+#define PCR_REG(p) (0x2004 + (p) * 0x100)
+#define PORT_MATRIX_S 16
+#define PORT_MATRIX_M 0xff0000
+
+#define PVC_REG(p) (0x2010 + (p) * 0x100)
+#define STAG_VPID_S 16
+#define STAG_VPID_M 0xffff0000
+#define VLAN_ATTR_S 6
+#define VLAN_ATTR_M 0xc0
+
+/* VLAN_ATTR: VLAN attributes */
+#define VLAN_ATTR_USER 0
+#define VLAN_ATTR_STACK 1
+#define VLAN_ATTR_TRANSLATION 2
+#define VLAN_ATTR_TRANSPARENT 3
+
+#define PMCR_REG(p) (0x3000 + (p) * 0x100)
+/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR
+ * MT7531 specific fields are defined below
+ */
+#define FORCE_MODE_EEE1G BIT(25)
+#define FORCE_MODE_EEE100 BIT(26)
+#define FORCE_MODE_TX_FC BIT(27)
+#define FORCE_MODE_RX_FC BIT(28)
+#define FORCE_MODE_DPX BIT(29)
+#define FORCE_MODE_SPD BIT(30)
+#define FORCE_MODE_LNK BIT(31)
+#define MT7531_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \
+ FORCE_MODE_DPX | FORCE_MODE_SPD | \
+ FORCE_MODE_LNK
+#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \
+ FORCE_MODE_DPX | FORCE_MODE_SPD | \
+ FORCE_MODE_LNK
+
+/* MT7531 SGMII Registers */
+#define MT7531_SGMII_REG_BASE 0x5000
+#define MT7531_SGMII_REG_PORT_BASE 0x1000
+#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
+ (p) * MT7531_SGMII_REG_PORT_BASE + (r))
+#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00)
+#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20)
+#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8)
+#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128)
+#define MT7531_PHYA_ANA_SYSPLL(p) MT7531_SGMII_REG(((p) - 5), 0x158)
+/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */
+
+/* MT753x System Control Register */
+#define SYS_CTRL_REG 0x7000
+#define SW_PHY_RST BIT(2)
+#define SW_SYS_RST BIT(1)
+#define SW_REG_RST BIT(0)
+
+/* MT7531 */
+#define MT7531_PHY_IAC 0x701c
+/* XXX: all fields are defined under GMAC_PIAC_REG */
+
+#define MT7531_CLKGEN_CTRL 0x7500
+#define CLK_SKEW_OUT_S 8
+#define CLK_SKEW_OUT_M 0x300
+#define CLK_SKEW_IN_S 6
+#define CLK_SKEW_IN_M 0xc0
+#define RXCLK_NO_DELAY BIT(5)
+#define TXCLK_NO_REVERSE BIT(4)
+#define GP_MODE_S 1
+#define GP_MODE_M 0x06
+#define GP_CLK_EN BIT(0)
+
+/* Values of GP_MODE */
+#define GP_MODE_RGMII 0
+#define GP_MODE_MII 1
+#define GP_MODE_REV_MII 2
+
+/* Values of CLK_SKEW_IN */
+#define CLK_SKEW_IN_NO_CHANGE 0
+#define CLK_SKEW_IN_DELAY_100PPS 1
+#define CLK_SKEW_IN_DELAY_200PPS 2
+#define CLK_SKEW_IN_REVERSE 3
+
+/* Values of CLK_SKEW_OUT */
+#define CLK_SKEW_OUT_NO_CHANGE 0
+#define CLK_SKEW_OUT_DELAY_100PPS 1
+#define CLK_SKEW_OUT_DELAY_200PPS 2
+#define CLK_SKEW_OUT_REVERSE 3
+
+#define HWTRAP_REG 0x7800
+/* MT7530 Modified Hardware Trap Status Registers */
+#define MHWTRAP_REG 0x7804
+#define CHG_TRAP BIT(16)
+#define LOOPDET_DIS BIT(14)
+#define P5_INTF_SEL_S 13
+#define P5_INTF_SEL_M 0x2000
+#define SMI_ADDR_S 11
+#define SMI_ADDR_M 0x1800
+#define XTAL_FSEL_S 9
+#define XTAL_FSEL_M 0x600
+#define P6_INTF_DIS BIT(8)
+#define P5_INTF_MODE_S 7
+#define P5_INTF_MODE_M 0x80
+#define P5_INTF_DIS BIT(6)
+#define C_MDIO_BPS BIT(5)
+#define CHIP_MODE_S 0
+#define CHIP_MODE_M 0x0f
+
+/* P5_INTF_SEL: Interface type of Port5 */
+#define P5_INTF_SEL_GPHY 0
+#define P5_INTF_SEL_GMAC5 1
+
+/* P5_INTF_MODE: Interface mode of Port5 */
+#define P5_INTF_MODE_GMII_MII 0
+#define P5_INTF_MODE_RGMII 1
+
+#define MT7530_P6ECR 0x7830
+#define P6_INTF_MODE_M 0x3
+#define P6_INTF_MODE_S 0
+
+/* P6_INTF_MODE: Interface mode of Port6 */
+#define P6_INTF_MODE_RGMII 0
+#define P6_INTF_MODE_TRGMII 1
+
+#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8)
+#define RD_TAP_S 0
+#define RD_TAP_M 0x7f
+
+#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8)
+/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */
+
+/* TOP Signals Status Register */
+#define MT7531_TOP_SIG_SR 0x780c
+#define PAD_MCM_SMI_EN BIT(0)
+#define PAD_DUAL_SGMII_EN BIT(1)
+
+/* MT7531 PLLGP Registers */
+#define MT7531_PLLGP_EN 0x7820
+#define EN_COREPLL BIT(2)
+#define SW_CLKSW BIT(1)
+#define SW_PLLGP BIT(0)
+
+#define MT7531_PLLGP_CR0 0x78a8
+#define RG_COREPLL_EN BIT(22)
+#define RG_COREPLL_POSDIV_S 23
+#define RG_COREPLL_POSDIV_M 0x3800000
+#define RG_COREPLL_SDM_PCW_S 1
+#define RG_COREPLL_SDM_PCW_M 0x3ffffe
+#define RG_COREPLL_SDM_PCW_CHG BIT(0)
+
+/* MT7531 RGMII and SGMII PLL clock */
+#define MT7531_ANA_PLLGP_CR2 0x78b0
+#define MT7531_ANA_PLLGP_CR5 0x78bc
+
+/* MT7531 GPIO GROUP IOLB SMT0 Control */
+#define MT7531_SMT0_IOLB 0x7f04
+#define SMT_IOLB_5_SMI_MDC_EN BIT(5)
+
+/* MT7530 GPHY MDIO MMD Registers */
+#define CORE_PLL_GROUP2 0x401
+#define RG_SYSPLL_EN_NORMAL BIT(15)
+#define RG_SYSPLL_VODEN BIT(14)
+#define RG_SYSPLL_POSDIV_S 5
+#define RG_SYSPLL_POSDIV_M 0x60
+
+#define CORE_PLL_GROUP4 0x403
+#define MT7531_BYPASS_MODE BIT(4)
+#define MT7531_POWER_ON_OFF BIT(5)
+#define RG_SYSPLL_DDSFBK_EN BIT(12)
+#define RG_SYSPLL_BIAS_EN BIT(11)
+#define RG_SYSPLL_BIAS_LPF_EN BIT(10)
+
+#define CORE_PLL_GROUP5 0x404
+#define RG_LCDDS_PCW_NCPO1_S 0
+#define RG_LCDDS_PCW_NCPO1_M 0xffff
+
+#define CORE_PLL_GROUP6 0x405
+#define RG_LCDDS_PCW_NCPO0_S 0
+#define RG_LCDDS_PCW_NCPO0_M 0xffff
+
+#define CORE_PLL_GROUP7 0x406
+#define RG_LCDDS_PWDB BIT(15)
+#define RG_LCDDS_ISO_EN BIT(13)
+#define RG_LCCDS_C_S 4
+#define RG_LCCDS_C_M 0x70
+#define RG_LCDDS_PCW_NCPO_CHG BIT(3)
+
+#define CORE_PLL_GROUP10 0x409
+#define RG_LCDDS_SSC_DELTA_S 0
+#define RG_LCDDS_SSC_DELTA_M 0xfff
+
+#define CORE_PLL_GROUP11 0x40a
+#define RG_LCDDS_SSC_DELTA1_S 0
+#define RG_LCDDS_SSC_DELTA1_M 0xfff
+
+#define CORE_GSWPLL_GRP1 0x40d
+#define RG_GSWPLL_POSDIV_200M_S 12
+#define RG_GSWPLL_POSDIV_200M_M 0x3000
+#define RG_GSWPLL_EN_PRE BIT(11)
+#define RG_GSWPLL_FBKDIV_200M_S 0
+#define RG_GSWPLL_FBKDIV_200M_M 0xff
+
+#define CORE_GSWPLL_GRP2 0x40e
+#define RG_GSWPLL_POSDIV_500M_S 8
+#define RG_GSWPLL_POSDIV_500M_M 0x300
+#define RG_GSWPLL_FBKDIV_500M_S 0
+#define RG_GSWPLL_FBKDIV_500M_M 0xff
+
+#define CORE_TRGMII_GSW_CLK_CG 0x410
+#define REG_GSWCK_EN BIT(0)
+#define REG_TRGMIICK_EN BIT(1)
+
+/* Extend PHY Control Register 3 */
+#define PHY_EXT_REG_14 0x14
+
+/* Fields of PHY_EXT_REG_14 */
+#define PHY_EN_DOWN_SHFIT BIT(4)
+
+/* Extend PHY Control Register 4 */
+#define PHY_EXT_REG_17 0x17
+
+/* Fields of PHY_EXT_REG_17 */
+#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4)
+
+/* PHY RXADC Control Register 7 */
+#define PHY_DEV1E_REG_0C6 0x0c6
+
+/* Fields of PHY_DEV1E_REG_0C6 */
+#define PHY_POWER_SAVING_S 8
+#define PHY_POWER_SAVING_M 0x300
+#define PHY_POWER_SAVING_TX 0x0
+
+struct mt753x_switch_priv {
+ struct mtk_eth_switch_priv epriv;
+ struct mii_dev *mdio_bus;
+ u32 smi_addr;
+ u32 phy_base;
+ u32 pmcr;
+
+ int (*reg_read)(struct mt753x_switch_priv *priv, u32 reg, u32 *data);
+ int (*reg_write)(struct mt753x_switch_priv *priv, u32 reg, u32 data);
+};
+
+int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg,
+ u32 *data);
+int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data);
+int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data);
+
+int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data);
+int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data);
+void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set);
+
+int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg);
+int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val);
+int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
+ u16 reg);
+int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
+ u16 reg, u16 val);
+
+int mt7531_mdio_register(struct mt753x_switch_priv *priv);
+
+void mt753x_port_isolation(struct mt753x_switch_priv *priv);
+
+#endif /* _MTK_ETH_MT753X_H_ */
diff --git a/drivers/net/mtk_eth/mt7988.c b/drivers/net/mtk_eth/mt7988.c
new file mode 100644
index 00000000000..a416d87840c
--- /dev/null
+++ b/drivers/net/mtk_eth/mt7988.c
@@ -0,0 +1,160 @@
+// 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 <miiphy.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/io.h>
+#include "mtk_eth.h"
+#include "mt753x.h"
+
+static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
+{
+ *data = readl(priv->epriv.ethsys_base + GSW_BASE + reg);
+
+ return 0;
+}
+
+static int mt7988_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
+{
+ writel(data, priv->epriv.ethsys_base + GSW_BASE + reg);
+
+ return 0;
+}
+
+static void mt7988_phy_setting(struct mt753x_switch_priv *priv)
+{
+ u16 val;
+ u32 i;
+
+ for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ /* Enable HW auto downshift */
+ mt7531_mii_write(priv, i, 0x1f, 0x1);
+ val = mt7531_mii_read(priv, i, PHY_EXT_REG_14);
+ val |= PHY_EN_DOWN_SHFIT;
+ mt7531_mii_write(priv, i, PHY_EXT_REG_14, val);
+
+ /* PHY link down power saving enable */
+ val = mt7531_mii_read(priv, i, PHY_EXT_REG_17);
+ val |= PHY_LINKDOWN_POWER_SAVING_EN;
+ mt7531_mii_write(priv, i, PHY_EXT_REG_17, val);
+ }
+}
+
+static void mt7988_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u32 pmcr = FORCE_MODE_LNK;
+
+ if (enable)
+ pmcr = priv->pmcr;
+
+ mt7988_reg_write(priv, PMCR_REG(6), pmcr);
+}
+
+static int mt7988_setup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+ u16 phy_addr, phy_val;
+ 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;
+
+ /* 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 |= BMCR_PDOWN;
+ mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+ }
+
+ switch (priv->epriv.phy_interface) {
+ case PHY_INTERFACE_MODE_USXGMII:
+ /* Use CPU bridge instead of actual USXGMII path */
+
+ /* Disable GDM1 RX CRC stripping */
+ /* mtk_fe_rmw(priv, 0x500, BIT(16), 0); */
+
+ /* Set GDM1 no drop */
+ mtk_fe_rmw(priv->epriv.eth, PSE_NO_DROP_CFG_REG, 0,
+ PSE_NO_DROP_GDM1);
+
+ /* Enable GSW CPU bridge as USXGMII */
+ /* mtk_fe_rmw(priv, 0x504, BIT(31), BIT(31)); */
+
+ /* Enable GDM1 to GSW CPU bridge */
+ mtk_gmac_rmw(priv->epriv.eth, GMAC_MAC_MISC_REG, 0, BIT(0));
+
+ /* XGMAC force link up */
+ mtk_gmac_rmw(priv->epriv.eth, GMAC_XGMAC_STS_REG, 0,
+ P1_XGMAC_FORCE_LINK);
+
+ /* Setup GSW CPU bridge IPG */
+ mtk_gmac_rmw(priv->epriv.eth, GMAC_GSW_CFG_REG,
+ GSWTX_IPG_M | GSWRX_IPG_M,
+ (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S));
+ break;
+ default:
+ printf("Error: MT7988 GSW does not support %s interface\n",
+ phy_string_for_interface(priv->epriv.phy_interface));
+ break;
+ }
+
+ pmcr = MT7988_FORCE_MODE |
+ (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+ MAC_MODE | MAC_TX_EN | MAC_RX_EN |
+ BKOFF_EN | BACKPR_EN |
+ FORCE_RX_FC | FORCE_TX_FC |
+ (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX |
+ FORCE_LINK;
+
+ priv->pmcr = pmcr;
+
+ /* Keep MAC link down before starting eth */
+ mt7988_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
+
+ /* Enable port isolation to block inter-port communication */
+ mt753x_port_isolation(priv);
+
+ /* 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 &= ~BMCR_PDOWN;
+ mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val);
+ }
+
+ mt7988_phy_setting(priv);
+
+ return mt7531_mdio_register(priv);
+}
+
+static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv)
+{
+ struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
+
+ mdio_unregister(priv->mdio_bus);
+
+ return 0;
+}
+
+MTK_ETH_SWITCH(mt7988) = {
+ .name = "mt7988",
+ .desc = "MediaTek MT7988 built-in switch",
+ .priv_size = sizeof(struct mt753x_switch_priv),
+ .reset_wait_time = 50,
+
+ .setup = mt7988_setup,
+ .cleanup = mt7531_cleanup,
+ .mac_control = mt7988_mac_control,
+};
diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth/mtk_eth.c
index 454caa3cd3a..5d6a42bceb4 100644
--- a/drivers/net/mtk_eth.c
+++ b/drivers/net/mtk_eth/mtk_eth.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2018 MediaTek Inc.
+ * Copyright (C) 2025 MediaTek Inc.
*
* Author: Weijie Gao <weijie.gao@mediatek.com>
* Author: Mark Lee <mark-mc.lee@mediatek.com>
@@ -35,14 +35,6 @@
#define RX_TOTAL_BUF_SIZE (NUM_RX_DESC * PKTSIZE_ALIGN)
#define TOTAL_PKT_BUF_SIZE (TX_TOTAL_BUF_SIZE + RX_TOTAL_BUF_SIZE)
-#define MT753X_NUM_PHYS 5
-#define MT753X_NUM_PORTS 7
-#define MT753X_DFL_SMI_ADDR 31
-#define MT753X_SMI_ADDR_MASK 0x1f
-
-#define MT753X_PHY_ADDR(base, addr) \
- (((base) + (addr)) & 0x1f)
-
#define GDMA_FWD_TO_CPU \
(0x20000000 | \
GDM_ICS_EN | \
@@ -75,32 +67,6 @@
(DP_DISCARD << MC_DP_S) | \
(DP_DISCARD << UN_DP_S))
-enum mtk_switch {
- SW_NONE,
- SW_MT7530,
- SW_MT7531,
- SW_MT7988,
-};
-
-/* struct mtk_soc_data - This is the structure holding all differences
- * among various plaforms
- * @caps Flags shown the extra capability for the SoC
- * @ana_rgc3: The offset for register ANA_RGC3 related to
- * sgmiisys syscon
- * @gdma_count: Number of GDMAs
- * @pdma_base: Register base of PDMA block
- * @txd_size: Tx DMA descriptor size.
- * @rxd_size: Rx DMA descriptor size.
- */
-struct mtk_soc_data {
- u32 caps;
- u32 ana_rgc3;
- u32 gdma_count;
- u32 pdma_base;
- u32 txd_size;
- u32 rxd_size;
-};
-
struct mtk_eth_priv {
char pkt_pool[TOTAL_PKT_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN);
@@ -113,7 +79,6 @@ struct mtk_eth_priv {
void __iomem *fe_base;
void __iomem *gmac_base;
void __iomem *sgmii_base;
- void __iomem *gsw_base;
struct regmap *ethsys_regmap;
@@ -125,11 +90,6 @@ struct mtk_eth_priv {
struct regmap *toprgu_regmap;
struct mii_dev *mdio_bus;
- int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg);
- int (*mii_write)(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 val);
- int (*mmd_read)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg);
- int (*mmd_write)(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg,
- u16 val);
const struct mtk_soc_data *soc;
int gmac_id;
@@ -143,13 +103,8 @@ struct mtk_eth_priv {
int phy_interface;
int phy_addr;
- enum mtk_switch sw;
- int (*switch_init)(struct mtk_eth_priv *priv);
- void (*switch_mac_control)(struct mtk_eth_priv *priv, bool enable);
- u32 mt753x_smi_addr;
- u32 mt753x_phy_base;
- u32 mt753x_pmcr;
- u32 mt753x_reset_wait_time;
+ struct mtk_eth_switch_priv *swpriv;
+ const char *swname;
struct gpio_desc rst_gpio;
int mcm;
@@ -184,7 +139,7 @@ static void mtk_gdma_write(struct mtk_eth_priv *priv, int no, u32 reg,
writel(val, priv->fe_base + gdma_base + reg);
}
-static void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
+void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
{
clrsetbits_le32(priv->fe_base + reg, clr, set);
}
@@ -199,13 +154,12 @@ static void mtk_gmac_write(struct mtk_eth_priv *priv, u32 reg, u32 val)
writel(val, priv->gmac_base + reg);
}
-static void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
+void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
{
clrsetbits_le32(priv->gmac_base + reg, clr, set);
}
-static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
- u32 set)
+void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
{
uint val;
@@ -226,16 +180,6 @@ static void mtk_infra_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
regmap_write(priv->infra_regmap, reg, val);
}
-static u32 mtk_gsw_read(struct mtk_eth_priv *priv, u32 reg)
-{
- return readl(priv->gsw_base + reg);
-}
-
-static void mtk_gsw_write(struct mtk_eth_priv *priv, u32 reg, u32 val)
-{
- writel(val, priv->gsw_base + reg);
-}
-
/* Direct MDIO clause 22/45 access via SoC */
static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data,
u32 cmd, u32 st)
@@ -269,19 +213,19 @@ static int mtk_mii_rw(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data,
}
/* Direct MDIO clause 22 read via SoC */
-static int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg)
+int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg)
{
return mtk_mii_rw(priv, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22);
}
/* Direct MDIO clause 22 write via SoC */
-static int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data)
+int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data)
{
return mtk_mii_rw(priv, phy, reg, data, MDIO_CMD_WRITE, MDIO_ST_C22);
}
/* Direct MDIO clause 45 read via SoC */
-static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg)
+int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg)
{
int ret;
@@ -294,8 +238,8 @@ static int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg)
}
/* Direct MDIO clause 45 write via SoC */
-static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad,
- u16 reg, u16 val)
+int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg,
+ u16 val)
{
int ret;
@@ -308,232 +252,52 @@ static int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad,
}
/* Indirect MDIO clause 45 read via MII registers */
-static int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad,
- u16 reg)
+int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg)
{
int ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
- (MMD_ADDR << MMD_CMD_S) |
- ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
+ ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
+ (MMD_ADDR << MMD_CMD_S) |
+ ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
if (ret)
return ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg);
+ ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg);
if (ret)
return ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
- (MMD_DATA << MMD_CMD_S) |
- ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
+ ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
+ (MMD_DATA << MMD_CMD_S) |
+ ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
if (ret)
return ret;
- return priv->mii_read(priv, addr, MII_MMD_ADDR_DATA_REG);
+ return mtk_mii_read(priv, addr, MII_MMD_ADDR_DATA_REG);
}
/* Indirect MDIO clause 45 write via MII registers */
-static int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad,
- u16 reg, u16 val)
+int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg,
+ u16 val)
{
int ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
- (MMD_ADDR << MMD_CMD_S) |
- ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
+ ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
+ (MMD_ADDR << MMD_CMD_S) |
+ ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
if (ret)
return ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg);
+ ret = mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, reg);
if (ret)
return ret;
- ret = priv->mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
- (MMD_DATA << MMD_CMD_S) |
- ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
+ ret = mtk_mii_write(priv, addr, MII_MMD_ACC_CTL_REG,
+ (MMD_DATA << MMD_CMD_S) |
+ ((devad << MMD_DEVAD_S) & MMD_DEVAD_M));
if (ret)
return ret;
- return priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val);
-}
-
-/*
- * MT7530 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 |
- * -------------------------------------------------------------------
- */
-
-static int mt753x_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data)
-{
- int ret, low_word, high_word;
-
- if (priv->sw == SW_MT7988) {
- *data = mtk_gsw_read(priv, reg);
- return 0;
- }
-
- /* Write page address */
- ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6);
- if (ret)
- return ret;
-
- /* Read low word */
- low_word = mtk_mii_read(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf);
- if (low_word < 0)
- return low_word;
-
- /* Read high word */
- high_word = mtk_mii_read(priv, priv->mt753x_smi_addr, 0x10);
- if (high_word < 0)
- return high_word;
-
- if (data)
- *data = ((u32)high_word << 16) | (low_word & 0xffff);
-
- return 0;
-}
-
-static int mt753x_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data)
-{
- int ret;
-
- if (priv->sw == SW_MT7988) {
- mtk_gsw_write(priv, reg, data);
- return 0;
- }
-
- /* Write page address */
- ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6);
- if (ret)
- return ret;
-
- /* Write low word */
- ret = mtk_mii_write(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf,
- data & 0xffff);
- if (ret)
- return ret;
-
- /* Write high word */
- return mtk_mii_write(priv, priv->mt753x_smi_addr, 0x10, data >> 16);
-}
-
-static void mt753x_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
- u32 set)
-{
- u32 val;
-
- mt753x_reg_read(priv, reg, &val);
- val &= ~clr;
- val |= set;
- mt753x_reg_write(priv, reg, val);
-}
-
-/* Indirect MDIO clause 22/45 access */
-static int mt7531_mii_rw(struct mtk_eth_priv *priv, int phy, int reg, u16 data,
- u32 cmd, u32 st)
-{
- ulong timeout;
- u32 val, timeout_ms;
- 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;
-}
-
-static int mt7531_mii_ind_read(struct mtk_eth_priv *priv, u8 phy, u8 reg)
-{
- u8 phy_addr;
-
- if (phy >= MT753X_NUM_PHYS)
- return -EINVAL;
-
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy);
-
- return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ,
- MDIO_ST_C22);
-}
-
-static int mt7531_mii_ind_write(struct mtk_eth_priv *priv, u8 phy, u8 reg,
- u16 val)
-{
- u8 phy_addr;
-
- if (phy >= MT753X_NUM_PHYS)
- return -EINVAL;
-
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy);
-
- return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE,
- MDIO_ST_C22);
-}
-
-static int mt7531_mmd_ind_read(struct mtk_eth_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->mt753x_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);
-}
-
-static int mt7531_mmd_ind_write(struct mtk_eth_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->mt753x_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);
+ return mtk_mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val);
}
static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
@@ -541,9 +305,9 @@ static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
struct mtk_eth_priv *priv = bus->priv;
if (devad < 0)
- return priv->mii_read(priv, addr, reg);
- else
- return priv->mmd_read(priv, addr, devad, reg);
+ return mtk_mii_read(priv, addr, reg);
+
+ return mtk_mmd_read(priv, addr, devad, reg);
}
static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
@@ -552,9 +316,9 @@ static int mtk_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
struct mtk_eth_priv *priv = bus->priv;
if (devad < 0)
- return priv->mii_write(priv, addr, reg, val);
- else
- return priv->mmd_write(priv, addr, devad, reg, val);
+ return mtk_mii_write(priv, addr, reg, val);
+
+ return mtk_mmd_write(priv, addr, devad, reg, val);
}
static int mtk_mdio_register(struct udevice *dev)
@@ -566,28 +330,6 @@ static int mtk_mdio_register(struct udevice *dev)
if (!mdio_bus)
return -ENOMEM;
- /* Assign MDIO access APIs according to the switch/phy */
- switch (priv->sw) {
- case SW_MT7530:
- priv->mii_read = mtk_mii_read;
- priv->mii_write = mtk_mii_write;
- priv->mmd_read = mtk_mmd_ind_read;
- priv->mmd_write = mtk_mmd_ind_write;
- break;
- case SW_MT7531:
- case SW_MT7988:
- priv->mii_read = mt7531_mii_ind_read;
- priv->mii_write = mt7531_mii_ind_write;
- priv->mmd_read = mt7531_mmd_ind_read;
- priv->mmd_write = mt7531_mmd_ind_write;
- break;
- default:
- priv->mii_read = mtk_mii_read;
- priv->mii_write = mtk_mii_write;
- priv->mmd_read = mtk_mmd_read;
- priv->mmd_write = mtk_mmd_write;
- }
-
mdio_bus->read = mtk_mdio_read;
mdio_bus->write = mtk_mdio_write;
snprintf(mdio_bus->name, sizeof(mdio_bus->name), dev->name);
@@ -604,531 +346,91 @@ static int mtk_mdio_register(struct udevice *dev)
return 0;
}
-static int mt753x_core_reg_read(struct mtk_eth_priv *priv, u32 reg)
-{
- u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0);
-
- return priv->mmd_read(priv, phy_addr, 0x1f, reg);
-}
-
-static void mt753x_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val)
-{
- u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0);
-
- priv->mmd_write(priv, phy_addr, 0x1f, reg, val);
-}
-
-static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode)
-{
- u32 ncpo1, ssc_delta;
-
- switch (mode) {
- case PHY_INTERFACE_MODE_RGMII:
- ncpo1 = 0x0c80;
- ssc_delta = 0x87;
- break;
- default:
- printf("error: xMII mode %d not supported\n", mode);
- return -EINVAL;
- }
-
- /* Disable MT7530 core clock */
- mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0);
-
- /* Disable MT7530 PLL */
- mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1,
- (2 << RG_GSWPLL_POSDIV_200M_S) |
- (32 << RG_GSWPLL_FBKDIV_200M_S));
-
- /* For MT7530 core clock = 500Mhz */
- mt753x_core_reg_write(priv, CORE_GSWPLL_GRP2,
- (1 << RG_GSWPLL_POSDIV_500M_S) |
- (25 << RG_GSWPLL_FBKDIV_500M_S));
-
- /* Enable MT7530 PLL */
- mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1,
- (2 << RG_GSWPLL_POSDIV_200M_S) |
- (32 << RG_GSWPLL_FBKDIV_200M_S) |
- RG_GSWPLL_EN_PRE);
-
- udelay(20);
-
- mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
-
- /* Setup the MT7530 TRGMII Tx Clock */
- mt753x_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1);
- mt753x_core_reg_write(priv, CORE_PLL_GROUP6, 0);
- mt753x_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta);
- mt753x_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta);
- mt753x_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
- RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN);
-
- mt753x_core_reg_write(priv, CORE_PLL_GROUP2,
- RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
- (1 << RG_SYSPLL_POSDIV_S));
-
- mt753x_core_reg_write(priv, CORE_PLL_GROUP7,
- RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) |
- RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
-
- /* Enable MT7530 core clock */
- mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG,
- REG_GSWCK_EN | REG_TRGMIICK_EN);
-
- return 0;
-}
-
-static void mt7530_mac_control(struct mtk_eth_priv *priv, bool enable)
-{
- u32 pmcr = FORCE_MODE;
-
- if (enable)
- pmcr = priv->mt753x_pmcr;
-
- mt753x_reg_write(priv, PMCR_REG(6), pmcr);
-}
-
-static int mt7530_setup(struct mtk_eth_priv *priv)
-{
- u16 phy_addr, phy_val;
- u32 val, txdrv;
- int i;
-
- if (!MTK_HAS_CAPS(priv->soc->caps, MTK_TRGMII_MT7621_CLK)) {
- /* Select 250MHz clk for RGMII mode */
- mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
- ETHSYS_TRGMII_CLK_SEL362_5, 0);
-
- txdrv = 8;
- } else {
- txdrv = 4;
- }
-
- /* Modify HWTRAP first to allow direct access to internal PHYs */
- mt753x_reg_read(priv, HWTRAP_REG, &val);
- val |= CHG_TRAP;
- val &= ~C_MDIO_BPS;
- mt753x_reg_write(priv, MHWTRAP_REG, val);
-
- /* Calculate the phy base address */
- val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3;
- priv->mt753x_phy_base = (val | 0x7) + 1;
-
- /* Turn off PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val |= BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
- }
-
- /* Force MAC link down before reset */
- mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
- mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE);
-
- /* MT7530 reset */
- mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
- udelay(100);
-
- val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
- MAC_MODE | FORCE_MODE |
- MAC_TX_EN | MAC_RX_EN |
- BKOFF_EN | BACKPR_EN |
- (SPEED_1000M << FORCE_SPD_S) |
- FORCE_DPX | FORCE_LINK;
-
- /* MT7530 Port6: Forced 1000M/FD, FC disabled */
- priv->mt753x_pmcr = val;
-
- /* MT7530 Port5: Forced link down */
- mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
-
- /* Keep MAC link down before starting eth */
- mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE);
-
- /* MT7530 Port6: Set to RGMII */
- mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII);
-
- /* Hardware Trap: Enable Port6, Disable Port5 */
- mt753x_reg_read(priv, HWTRAP_REG, &val);
- val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS |
- (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) |
- (P5_INTF_MODE_RGMII << P5_INTF_MODE_S);
- val &= ~(C_MDIO_BPS | P6_INTF_DIS);
- mt753x_reg_write(priv, MHWTRAP_REG, val);
-
- /* Setup switch core pll */
- mt7530_pad_clk_setup(priv, priv->phy_interface);
-
- /* Lower Tx Driving for TRGMII path */
- for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
- mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
- (txdrv << TD_DM_DRVP_S) |
- (txdrv << TD_DM_DRVN_S));
-
- for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
- mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
-
- /* Turn on PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val &= ~BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
- }
-
- return 0;
-}
-
-static void mt7531_core_pll_setup(struct mtk_eth_priv *priv, int mcm)
+static int mtk_switch_init(struct mtk_eth_priv *priv)
{
- /* Step 1 : Disable MT7531 COREPLL */
- mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0);
-
- /* Step 2: switch to XTAL output */
- mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW);
-
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0);
-
- /* Step 3: disable PLLGP and enable program PLLGP */
- mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP);
-
- /* Step 4: program COREPLL output frequency to 500MHz */
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M,
- 2 << RG_COREPLL_POSDIV_S);
- udelay(25);
-
- /* Currently, support XTAL 25Mhz only */
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M,
- 0x140000 << RG_COREPLL_SDM_PCW_S);
-
- /* Set feedback divide ratio update signal to high */
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG,
- RG_COREPLL_SDM_PCW_CHG);
-
- /* Wait for at least 16 XTAL clocks */
- udelay(10);
-
- /* Step 5: set feedback divide ratio update signal to low */
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0);
-
- /* add enable 325M clock for SGMII */
- mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000);
-
- /* add enable 250SSC clock for RGMII */
- mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000);
-
- /*Step 6: Enable MT7531 PLL */
- mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN);
-
- mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL);
-
- udelay(25);
-}
-
-static int mt7531_port_sgmii_init(struct mtk_eth_priv *priv,
- u32 port)
-{
- if (port != 5 && port != 6) {
- printf("mt7531: port %d is not a SGMII port\n", port);
- return -EINVAL;
- }
-
- /* Set SGMII GEN2 speed(2.5G) */
- mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), SGMSYS_SPEED_MASK,
- FIELD_PREP(SGMSYS_SPEED_MASK, SGMSYS_SPEED_2500));
-
- /* Disable SGMII AN */
- mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port),
- SGMII_AN_ENABLE, 0);
-
- /* SGMII force mode setting */
- mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE);
-
- /* Release PHYA power down state */
- mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
- SGMII_PHYA_PWD, 0);
-
- return 0;
-}
-
-static int mt7531_port_rgmii_init(struct mtk_eth_priv *priv, u32 port)
-{
- u32 val;
-
- if (port != 5) {
- printf("error: RGMII mode is not available for port %d\n",
- port);
- return -EINVAL;
- }
-
- mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val);
- val |= GP_CLK_EN;
- val &= ~GP_MODE_M;
- val |= GP_MODE_RGMII << GP_MODE_S;
- val |= TXCLK_NO_REVERSE;
- val |= RXCLK_NO_DELAY;
- val &= ~CLK_SKEW_IN_M;
- val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S;
- val &= ~CLK_SKEW_OUT_M;
- val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S;
- mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val);
-
- return 0;
-}
-
-static void mt7531_phy_setting(struct mtk_eth_priv *priv)
-{
- int i;
- u32 val;
-
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- /* Enable HW auto downshift */
- priv->mii_write(priv, i, 0x1f, 0x1);
- val = priv->mii_read(priv, i, PHY_EXT_REG_14);
- val |= PHY_EN_DOWN_SHFIT;
- priv->mii_write(priv, i, PHY_EXT_REG_14, val);
-
- /* PHY link down power saving enable */
- val = priv->mii_read(priv, i, PHY_EXT_REG_17);
- val |= PHY_LINKDOWN_POWER_SAVING_EN;
- priv->mii_write(priv, i, PHY_EXT_REG_17, val);
-
- val = priv->mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6);
- val &= ~PHY_POWER_SAVING_M;
- val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S;
- priv->mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val);
- }
-}
-
-static void mt7531_mac_control(struct mtk_eth_priv *priv, bool enable)
-{
- u32 pmcr = FORCE_MODE_LNK;
-
- if (enable)
- pmcr = priv->mt753x_pmcr;
-
- mt753x_reg_write(priv, PMCR_REG(5), pmcr);
- mt753x_reg_write(priv, PMCR_REG(6), pmcr);
-}
-
-static int mt7531_setup(struct mtk_eth_priv *priv)
-{
- u16 phy_addr, phy_val;
- u32 val;
- u32 pmcr;
- u32 port5_sgmii;
- int i;
-
- priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) &
- MT753X_SMI_ADDR_MASK;
-
- /* Turn off PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val |= BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
- }
-
- /* Force MAC link down before reset */
- mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK);
- mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
-
- /* Switch soft reset */
- mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
- udelay(100);
-
- /* Enable MDC input Schmitt Trigger */
- mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN,
- SMT_IOLB_5_SMI_MDC_EN);
-
- mt7531_core_pll_setup(priv, priv->mcm);
-
- mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val);
- port5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
-
- /* port5 support either RGMII or SGMII, port6 only support SGMII. */
- switch (priv->phy_interface) {
- case PHY_INTERFACE_MODE_RGMII:
- if (!port5_sgmii)
- mt7531_port_rgmii_init(priv, 5);
- break;
- case PHY_INTERFACE_MODE_2500BASEX:
- mt7531_port_sgmii_init(priv, 6);
- if (port5_sgmii)
- mt7531_port_sgmii_init(priv, 5);
- break;
- default:
- break;
- }
-
- pmcr = MT7531_FORCE_MODE |
- (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
- MAC_MODE | MAC_TX_EN | MAC_RX_EN |
- BKOFF_EN | BACKPR_EN |
- FORCE_RX_FC | FORCE_TX_FC |
- (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX |
- FORCE_LINK;
-
- priv->mt753x_pmcr = pmcr;
-
- /* Keep MAC link down before starting eth */
- mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK);
- mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
-
- /* Turn on PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val &= ~BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
- }
-
- mt7531_phy_setting(priv);
-
- /* Enable Internal PHYs */
- val = mt753x_core_reg_read(priv, CORE_PLL_GROUP4);
- val |= MT7531_BYPASS_MODE;
- val &= ~MT7531_POWER_ON_OFF;
- mt753x_core_reg_write(priv, CORE_PLL_GROUP4, val);
-
- return 0;
-}
-
-static void mt7988_phy_setting(struct mtk_eth_priv *priv)
-{
- u16 val;
- u32 i;
-
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- /* Enable HW auto downshift */
- priv->mii_write(priv, i, 0x1f, 0x1);
- val = priv->mii_read(priv, i, PHY_EXT_REG_14);
- val |= PHY_EN_DOWN_SHFIT;
- priv->mii_write(priv, i, PHY_EXT_REG_14, val);
-
- /* PHY link down power saving enable */
- val = priv->mii_read(priv, i, PHY_EXT_REG_17);
- val |= PHY_LINKDOWN_POWER_SAVING_EN;
- priv->mii_write(priv, i, PHY_EXT_REG_17, val);
- }
-}
-
-static void mt7988_mac_control(struct mtk_eth_priv *priv, bool enable)
-{
- u32 pmcr = FORCE_MODE_LNK;
-
- if (enable)
- pmcr = priv->mt753x_pmcr;
-
- mt753x_reg_write(priv, PMCR_REG(6), pmcr);
-}
-
-static int mt7988_setup(struct mtk_eth_priv *priv)
-{
- u16 phy_addr, phy_val;
- u32 pmcr;
- int i;
-
- priv->gsw_base = regmap_get_range(priv->ethsys_regmap, 0) + GSW_BASE;
-
- priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) &
- MT753X_SMI_ADDR_MASK;
-
- /* Turn off PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val |= BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
- }
-
- switch (priv->phy_interface) {
- case PHY_INTERFACE_MODE_USXGMII:
- /* Use CPU bridge instead of actual USXGMII path */
-
- /* Set GDM1 no drop */
- mtk_fe_rmw(priv, PSE_NO_DROP_CFG_REG, 0, PSE_NO_DROP_GDM1);
-
- /* Enable GDM1 to GSW CPU bridge */
- mtk_gmac_rmw(priv, GMAC_MAC_MISC_REG, 0, BIT(0));
-
- /* XGMAC force link up */
- mtk_gmac_rmw(priv, GMAC_XGMAC_STS_REG, 0, P1_XGMAC_FORCE_LINK);
-
- /* Setup GSW CPU bridge IPG */
- mtk_gmac_rmw(priv, GMAC_GSW_CFG_REG, GSWTX_IPG_M | GSWRX_IPG_M,
- (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S));
- break;
- default:
- printf("Error: MT7988 GSW does not support %s interface\n",
- phy_string_for_interface(priv->phy_interface));
- break;
- }
+ struct mtk_eth_switch *swdrvs = ll_entry_start(struct mtk_eth_switch,
+ mtk_eth_switch);
+ const u32 n_swdrvs = ll_entry_count(struct mtk_eth_switch,
+ mtk_eth_switch);
+ struct mtk_eth_switch *tmp, *swdrv = NULL;
+ u32 reset_wait_time = 500;
+ size_t priv_size;
+ int ret;
- pmcr = MT7988_FORCE_MODE |
- (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
- MAC_MODE | MAC_TX_EN | MAC_RX_EN |
- BKOFF_EN | BACKPR_EN |
- FORCE_RX_FC | FORCE_TX_FC |
- (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX |
- FORCE_LINK;
-
- priv->mt753x_pmcr = pmcr;
-
- /* Keep MAC link down before starting eth */
- mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
-
- /* Turn on PHYs */
- for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
- phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
- phy_val &= ~BMCR_PDOWN;
- priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
+ if (strcmp(priv->swname, "auto")) {
+ for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) {
+ if (!strcmp(tmp->name, priv->swname)) {
+ swdrv = tmp;
+ break;
+ }
+ }
}
- mt7988_phy_setting(priv);
-
- return 0;
-}
-
-static int mt753x_switch_init(struct mtk_eth_priv *priv)
-{
- int ret;
- int i;
+ if (swdrv)
+ reset_wait_time = swdrv->reset_wait_time;
/* Global reset switch */
if (priv->mcm) {
reset_assert(&priv->rst_mcm);
udelay(1000);
reset_deassert(&priv->rst_mcm);
- mdelay(priv->mt753x_reset_wait_time);
+ mdelay(reset_wait_time);
} else if (dm_gpio_is_valid(&priv->rst_gpio)) {
dm_gpio_set_value(&priv->rst_gpio, 0);
udelay(1000);
dm_gpio_set_value(&priv->rst_gpio, 1);
- mdelay(priv->mt753x_reset_wait_time);
+ mdelay(reset_wait_time);
}
- ret = priv->switch_init(priv);
- if (ret)
- return ret;
+ if (!swdrv) {
+ for (tmp = swdrvs; tmp < swdrvs + n_swdrvs; tmp++) {
+ if (!tmp->detect)
+ continue;
- /* Set port isolation */
- 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));
+ ret = tmp->detect(priv);
+ if (!ret) {
+ swdrv = tmp;
+ break;
+ }
+ }
+
+ if (!swdrv) {
+ printf("Error: unable to detect switch\n");
+ return -ENODEV;
+ }
+ } else {
+ if (swdrv->detect) {
+ ret = swdrv->detect(priv);
+ if (ret) {
+ printf("Error: switch probing failed\n");
+ return -ENODEV;
+ }
+ }
+ }
+
+ printf("%s\n", swdrv->desc);
+
+ priv_size = swdrv->priv_size;
+ if (priv_size < sizeof(struct mtk_eth_switch_priv))
+ priv_size = sizeof(struct mtk_eth_switch_priv);
+
+ priv->swpriv = calloc(1, priv_size);
+ if (!priv->swpriv) {
+ printf("Error: no memory for switch data\n");
+ return -ENOMEM;
+ }
+
+ priv->swpriv->eth = priv;
+ priv->swpriv->soc = priv->soc;
+ priv->swpriv->phy_interface = priv->phy_interface;
+ priv->swpriv->sw = swdrv;
+ priv->swpriv->ethsys_base = regmap_get_range(priv->ethsys_regmap, 0);
- /* Set port mode to user port */
- mt753x_reg_write(priv, PVC_REG(i),
- (0x8100 << STAG_VPID_S) |
- (VLAN_ATTR_USER << VLAN_ATTR_S));
+ ret = swdrv->setup(priv->swpriv);
+ if (ret) {
+ free(priv->swpriv);
+ priv->swpriv = NULL;
+ return ret;
}
return 0;
@@ -1759,7 +1061,8 @@ static int mtk_eth_start(struct udevice *dev)
}
if (MTK_HAS_CAPS(priv->soc->caps, MTK_NETSYS_V3)) {
- if (priv->sw == SW_MT7988 && priv->gmac_id == 0) {
+ if (priv->swpriv && !strcmp(priv->swpriv->sw->name, "mt7988") &&
+ priv->gmac_id == 0) {
mtk_gdma_write(priv, priv->gmac_id, GDMA_IG_CTRL_REG,
GDMA_BRIDGE_TO_CPU);
@@ -1778,11 +1081,12 @@ static int mtk_eth_start(struct udevice *dev)
mtk_eth_fifo_init(priv);
- if (priv->switch_mac_control)
- priv->switch_mac_control(priv, true);
-
- /* Start PHY */
- if (priv->sw == SW_NONE) {
+ if (priv->swpriv) {
+ /* Enable communication with switch */
+ if (priv->swpriv->sw->mac_control)
+ priv->swpriv->sw->mac_control(priv->swpriv, true);
+ } else {
+ /* Start PHY */
ret = mtk_phy_start(priv);
if (ret)
return ret;
@@ -1799,8 +1103,10 @@ static void mtk_eth_stop(struct udevice *dev)
{
struct mtk_eth_priv *priv = dev_get_priv(dev);
- if (priv->switch_mac_control)
- priv->switch_mac_control(priv, false);
+ if (priv->swpriv) {
+ if (priv->swpriv->sw->mac_control)
+ priv->swpriv->sw->mac_control(priv->swpriv, false);
+ }
mtk_pdma_rmw(priv, PDMA_GLO_CFG_REG,
TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN, 0);
@@ -1953,11 +1259,11 @@ static int mtk_eth_probe(struct udevice *dev)
return ret;
/* Probe phy if switch is not specified */
- if (priv->sw == SW_NONE)
+ if (!priv->swname)
return mtk_phy_probe(dev);
/* Initialize switch */
- return mt753x_switch_init(priv);
+ return mtk_switch_init(priv);
}
static int mtk_eth_remove(struct udevice *dev)
@@ -1971,6 +1277,12 @@ static int mtk_eth_remove(struct udevice *dev)
/* Stop possibly started DMA */
mtk_eth_stop(dev);
+ if (priv->swpriv) {
+ if (priv->swpriv->sw->cleanup)
+ priv->swpriv->sw->cleanup(priv->swpriv);
+ free(priv->swpriv);
+ }
+
return 0;
}
@@ -1980,7 +1292,6 @@ static int mtk_eth_of_to_plat(struct udevice *dev)
struct mtk_eth_priv *priv = dev_get_priv(dev);
struct ofnode_phandle_args args;
struct regmap *regmap;
- const char *str;
ofnode subnode;
int ret;
@@ -2126,36 +1437,8 @@ static int mtk_eth_of_to_plat(struct udevice *dev)
return PTR_ERR(priv->toprgu_regmap);
}
- /* check for switch first, otherwise phy will be used */
- priv->sw = SW_NONE;
- priv->switch_init = NULL;
- priv->switch_mac_control = NULL;
- str = dev_read_string(dev, "mediatek,switch");
-
- if (str) {
- if (!strcmp(str, "mt7530")) {
- priv->sw = SW_MT7530;
- priv->switch_init = mt7530_setup;
- priv->switch_mac_control = mt7530_mac_control;
- priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR;
- priv->mt753x_reset_wait_time = 1000;
- } else if (!strcmp(str, "mt7531")) {
- priv->sw = SW_MT7531;
- priv->switch_init = mt7531_setup;
- priv->switch_mac_control = mt7531_mac_control;
- priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR;
- priv->mt753x_reset_wait_time = 200;
- } else if (!strcmp(str, "mt7988")) {
- priv->sw = SW_MT7988;
- priv->switch_init = mt7988_setup;
- priv->switch_mac_control = mt7988_mac_control;
- priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR;
- priv->mt753x_reset_wait_time = 50;
- } else {
- printf("error: unsupported switch\n");
- return -EINVAL;
- }
-
+ priv->swname = dev_read_string(dev, "mediatek,switch");
+ if (priv->swname) {
priv->mcm = dev_read_bool(dev, "mediatek,mcm");
if (priv->mcm) {
ret = reset_get_by_name(dev, "mcm", &priv->rst_mcm);
@@ -2194,6 +1477,15 @@ static const struct mtk_soc_data mt7988_data = {
.rxd_size = sizeof(struct mtk_rx_dma_v2),
};
+static const struct mtk_soc_data mt7987_data = {
+ .caps = MT7987_CAPS,
+ .ana_rgc3 = 0x128,
+ .gdma_count = 3,
+ .pdma_base = PDMA_V3_BASE,
+ .txd_size = sizeof(struct mtk_tx_dma_v2),
+ .rxd_size = sizeof(struct mtk_rx_dma_v2),
+};
+
static const struct mtk_soc_data mt7986_data = {
.caps = MT7986_CAPS,
.ana_rgc3 = 0x128,
@@ -2248,6 +1540,7 @@ static const struct mtk_soc_data mt7621_data = {
static const struct udevice_id mtk_eth_ids[] = {
{ .compatible = "mediatek,mt7988-eth", .data = (ulong)&mt7988_data },
+ { .compatible = "mediatek,mt7987-eth", .data = (ulong)&mt7987_data },
{ .compatible = "mediatek,mt7986-eth", .data = (ulong)&mt7986_data },
{ .compatible = "mediatek,mt7981-eth", .data = (ulong)&mt7981_data },
{ .compatible = "mediatek,mt7629-eth", .data = (ulong)&mt7629_data },
@@ -2271,10 +1564,10 @@ U_BOOT_DRIVER(mtk_eth) = {
.id = UCLASS_ETH,
.of_match = mtk_eth_ids,
.of_to_plat = mtk_eth_of_to_plat,
- .plat_auto = sizeof(struct eth_pdata),
+ .plat_auto = sizeof(struct eth_pdata),
.probe = mtk_eth_probe,
.remove = mtk_eth_remove,
.ops = &mtk_eth_ops,
- .priv_auto = sizeof(struct mtk_eth_priv),
+ .priv_auto = sizeof(struct mtk_eth_priv),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
diff --git a/drivers/net/mtk_eth.h b/drivers/net/mtk_eth/mtk_eth.h
index 1aa037907c5..0ad32799128 100644
--- a/drivers/net/mtk_eth.h
+++ b/drivers/net/mtk_eth/mtk_eth.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) 2018 MediaTek Inc.
+ * Copyright (C) 2025 MediaTek Inc.
*
* Author: Weijie Gao <weijie.gao@mediatek.com>
* Author: Mark Lee <mark-mc.lee@mediatek.com>
@@ -9,9 +9,55 @@
#ifndef _MTK_ETH_H_
#define _MTK_ETH_H_
+#include <linker_lists.h>
#include <linux/bitops.h>
#include <linux/bitfield.h>
+struct mtk_eth_priv;
+struct mtk_eth_switch_priv;
+
+/* struct mtk_soc_data - This is the structure holding all differences
+ * among various plaforms
+ * @caps Flags shown the extra capability for the SoC
+ * @ana_rgc3: The offset for register ANA_RGC3 related to
+ * sgmiisys syscon
+ * @gdma_count: Number of GDMAs
+ * @pdma_base: Register base of PDMA block
+ * @txd_size: Tx DMA descriptor size.
+ * @rxd_size: Rx DMA descriptor size.
+ */
+struct mtk_soc_data {
+ u32 caps;
+ u32 ana_rgc3;
+ u32 gdma_count;
+ u32 pdma_base;
+ u32 txd_size;
+ u32 rxd_size;
+};
+
+struct mtk_eth_switch {
+ const char *name;
+ const char *desc;
+ size_t priv_size;
+ u32 reset_wait_time;
+
+ int (*detect)(struct mtk_eth_priv *priv);
+ int (*setup)(struct mtk_eth_switch_priv *priv);
+ int (*cleanup)(struct mtk_eth_switch_priv *priv);
+ void (*mac_control)(struct mtk_eth_switch_priv *priv, bool enable);
+};
+
+#define MTK_ETH_SWITCH(__name) \
+ ll_entry_declare(struct mtk_eth_switch, __name, mtk_eth_switch)
+
+struct mtk_eth_switch_priv {
+ struct mtk_eth_priv *eth;
+ const struct mtk_eth_switch *sw;
+ const struct mtk_soc_data *soc;
+ void *ethsys_base;
+ int phy_interface;
+};
+
enum mkt_eth_capabilities {
MTK_TRGMII_BIT,
MTK_TRGMII_MT7621_CLK_BIT,
@@ -36,7 +82,6 @@ enum mkt_eth_capabilities {
/* Supported path present on SoCs */
#define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT)
-
#define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT)
#define MTK_ETH_PATH_MT7622_SGMII BIT(MTK_ETH_PATH_MT7622_SGMII_BIT)
#define MTK_ETH_PATH_MT7629_GMAC2 BIT(MTK_ETH_PATH_MT7629_GMAC2_BIT)
@@ -59,6 +104,8 @@ enum mkt_eth_capabilities {
#define MT7986_CAPS (MTK_NETSYS_V2)
+#define MT7987_CAPS (MTK_NETSYS_V3 | MTK_GMAC2_U3_QPHY | MTK_INFRA)
+
#define MT7988_CAPS (MTK_NETSYS_V3 | MTK_INFRA)
/* Frame Engine Register Bases */
@@ -72,7 +119,6 @@ enum mkt_eth_capabilities {
#define GSW_BASE 0x20000
/* Ethernet subsystem registers */
-
#define ETHSYS_SYSCFG1_REG 0x14
#define SYSCFG1_GE_MODE_S(n) (12 + ((n) * 2))
#define SYSCFG1_GE_MODE_M 0x3
@@ -191,7 +237,6 @@ enum mkt_eth_capabilities {
#define DP_DISCARD 7
/* GMAC Registers */
-
#define GMAC_PPSC_REG 0x0000
#define PHY_MDC_CFG GENMASK(29, 24)
#define MDC_TURBO BIT(20)
@@ -272,6 +317,8 @@ enum mkt_eth_capabilities {
#define RX_RST BIT(31)
#define RXC_DQSISEL BIT(30)
+#define NUM_TRGMII_CTRL 5
+
#define GMAC_TRGMII_TD_ODT(n) (0x354 + (n) * 8)
#define TD_DM_DRVN_S 4
#define TD_DM_DRVN_M 0xf0
@@ -288,164 +335,7 @@ enum mkt_eth_capabilities {
#define XGMAC_FORCE_TX_FC BIT(5)
#define XGMAC_FORCE_RX_FC BIT(4)
-/* MT7530 Registers */
-
-#define PCR_REG(p) (0x2004 + (p) * 0x100)
-#define PORT_MATRIX_S 16
-#define PORT_MATRIX_M 0xff0000
-
-#define PVC_REG(p) (0x2010 + (p) * 0x100)
-#define STAG_VPID_S 16
-#define STAG_VPID_M 0xffff0000
-#define VLAN_ATTR_S 6
-#define VLAN_ATTR_M 0xc0
-
-/* VLAN_ATTR: VLAN attributes */
-#define VLAN_ATTR_USER 0
-#define VLAN_ATTR_STACK 1
-#define VLAN_ATTR_TRANSLATION 2
-#define VLAN_ATTR_TRANSPARENT 3
-
-#define PMCR_REG(p) (0x3000 + (p) * 0x100)
-/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR
- * MT7531 specific fields are defined below
- */
-#define FORCE_MODE_EEE1G BIT(25)
-#define FORCE_MODE_EEE100 BIT(26)
-#define FORCE_MODE_TX_FC BIT(27)
-#define FORCE_MODE_RX_FC BIT(28)
-#define FORCE_MODE_DPX BIT(29)
-#define FORCE_MODE_SPD BIT(30)
-#define FORCE_MODE_LNK BIT(31)
-#define MT7531_FORCE_MODE FORCE_MODE_EEE1G | FORCE_MODE_EEE100 |\
- FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \
- FORCE_MODE_DPX | FORCE_MODE_SPD | \
- FORCE_MODE_LNK
-#define MT7988_FORCE_MODE FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \
- FORCE_MODE_DPX | FORCE_MODE_SPD | \
- FORCE_MODE_LNK
-
-/* MT7531 SGMII Registers */
-#define MT7531_SGMII_REG_BASE 0x5000
-#define MT7531_SGMII_REG_PORT_BASE 0x1000
-#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
- (p) * MT7531_SGMII_REG_PORT_BASE + (r))
-#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(((p) - 5), 0x00)
-#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(((p) - 5), 0x20)
-#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8)
-#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(((p) - 5), 0x128)
-/* XXX: all fields of MT7531 SGMII are defined under SGMSYS */
-
-/* MT753x System Control Register */
-#define SYS_CTRL_REG 0x7000
-#define SW_PHY_RST BIT(2)
-#define SW_SYS_RST BIT(1)
-#define SW_REG_RST BIT(0)
-
-/* MT7531 */
-#define MT7531_PHY_IAC 0x701c
-/* XXX: all fields are defined under GMAC_PIAC_REG */
-
-#define MT7531_CLKGEN_CTRL 0x7500
-#define CLK_SKEW_OUT_S 8
-#define CLK_SKEW_OUT_M 0x300
-#define CLK_SKEW_IN_S 6
-#define CLK_SKEW_IN_M 0xc0
-#define RXCLK_NO_DELAY BIT(5)
-#define TXCLK_NO_REVERSE BIT(4)
-#define GP_MODE_S 1
-#define GP_MODE_M 0x06
-#define GP_CLK_EN BIT(0)
-
-/* Values of GP_MODE */
-#define GP_MODE_RGMII 0
-#define GP_MODE_MII 1
-#define GP_MODE_REV_MII 2
-
-/* Values of CLK_SKEW_IN */
-#define CLK_SKEW_IN_NO_CHANGE 0
-#define CLK_SKEW_IN_DELAY_100PPS 1
-#define CLK_SKEW_IN_DELAY_200PPS 2
-#define CLK_SKEW_IN_REVERSE 3
-
-/* Values of CLK_SKEW_OUT */
-#define CLK_SKEW_OUT_NO_CHANGE 0
-#define CLK_SKEW_OUT_DELAY_100PPS 1
-#define CLK_SKEW_OUT_DELAY_200PPS 2
-#define CLK_SKEW_OUT_REVERSE 3
-
-#define HWTRAP_REG 0x7800
-/* MT7530 Modified Hardware Trap Status Registers */
-#define MHWTRAP_REG 0x7804
-#define CHG_TRAP BIT(16)
-#define LOOPDET_DIS BIT(14)
-#define P5_INTF_SEL_S 13
-#define P5_INTF_SEL_M 0x2000
-#define SMI_ADDR_S 11
-#define SMI_ADDR_M 0x1800
-#define XTAL_FSEL_S 9
-#define XTAL_FSEL_M 0x600
-#define P6_INTF_DIS BIT(8)
-#define P5_INTF_MODE_S 7
-#define P5_INTF_MODE_M 0x80
-#define P5_INTF_DIS BIT(6)
-#define C_MDIO_BPS BIT(5)
-#define CHIP_MODE_S 0
-#define CHIP_MODE_M 0x0f
-
-/* P5_INTF_SEL: Interface type of Port5 */
-#define P5_INTF_SEL_GPHY 0
-#define P5_INTF_SEL_GMAC5 1
-
-/* P5_INTF_MODE: Interface mode of Port5 */
-#define P5_INTF_MODE_GMII_MII 0
-#define P5_INTF_MODE_RGMII 1
-
-#define MT7530_P6ECR 0x7830
-#define P6_INTF_MODE_M 0x3
-#define P6_INTF_MODE_S 0
-
-/* P6_INTF_MODE: Interface mode of Port6 */
-#define P6_INTF_MODE_RGMII 0
-#define P6_INTF_MODE_TRGMII 1
-
-#define NUM_TRGMII_CTRL 5
-
-#define MT7530_TRGMII_RD(n) (0x7a10 + (n) * 8)
-#define RD_TAP_S 0
-#define RD_TAP_M 0x7f
-
-#define MT7530_TRGMII_TD_ODT(n) (0x7a54 + (n) * 8)
-/* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */
-
-/* TOP Signals Status Register */
-#define MT7531_TOP_SIG_SR 0x780c
-#define PAD_MCM_SMI_EN BIT(0)
-#define PAD_DUAL_SGMII_EN BIT(1)
-
-/* MT7531 PLLGP Registers */
-#define MT7531_PLLGP_EN 0x7820
-#define EN_COREPLL BIT(2)
-#define SW_CLKSW BIT(1)
-#define SW_PLLGP BIT(0)
-
-#define MT7531_PLLGP_CR0 0x78a8
-#define RG_COREPLL_EN BIT(22)
-#define RG_COREPLL_POSDIV_S 23
-#define RG_COREPLL_POSDIV_M 0x3800000
-#define RG_COREPLL_SDM_PCW_S 1
-#define RG_COREPLL_SDM_PCW_M 0x3ffffe
-#define RG_COREPLL_SDM_PCW_CHG BIT(0)
-
-/* MT7531 RGMII and SGMII PLL clock */
-#define MT7531_ANA_PLLGP_CR2 0x78b0
-#define MT7531_ANA_PLLGP_CR5 0x78bc
-
-/* MT7531 GPIO GROUP IOLB SMT0 Control */
-#define MT7531_SMT0_IOLB 0x7f04
-#define SMT_IOLB_5_SMI_MDC_EN BIT(5)
-
-/* MT7530 GPHY MDIO Indirect Access Registers */
+/* MDIO Indirect Access Registers */
#define MII_MMD_ACC_CTL_REG 0x0d
#define MMD_CMD_S 14
#define MMD_CMD_M 0xc000
@@ -460,80 +350,6 @@ enum mkt_eth_capabilities {
#define MII_MMD_ADDR_DATA_REG 0x0e
-/* MT7530 GPHY MDIO MMD Registers */
-#define CORE_PLL_GROUP2 0x401
-#define RG_SYSPLL_EN_NORMAL BIT(15)
-#define RG_SYSPLL_VODEN BIT(14)
-#define RG_SYSPLL_POSDIV_S 5
-#define RG_SYSPLL_POSDIV_M 0x60
-
-#define CORE_PLL_GROUP4 0x403
-#define MT7531_BYPASS_MODE BIT(4)
-#define MT7531_POWER_ON_OFF BIT(5)
-#define RG_SYSPLL_DDSFBK_EN BIT(12)
-#define RG_SYSPLL_BIAS_EN BIT(11)
-#define RG_SYSPLL_BIAS_LPF_EN BIT(10)
-
-#define CORE_PLL_GROUP5 0x404
-#define RG_LCDDS_PCW_NCPO1_S 0
-#define RG_LCDDS_PCW_NCPO1_M 0xffff
-
-#define CORE_PLL_GROUP6 0x405
-#define RG_LCDDS_PCW_NCPO0_S 0
-#define RG_LCDDS_PCW_NCPO0_M 0xffff
-
-#define CORE_PLL_GROUP7 0x406
-#define RG_LCDDS_PWDB BIT(15)
-#define RG_LCDDS_ISO_EN BIT(13)
-#define RG_LCCDS_C_S 4
-#define RG_LCCDS_C_M 0x70
-#define RG_LCDDS_PCW_NCPO_CHG BIT(3)
-
-#define CORE_PLL_GROUP10 0x409
-#define RG_LCDDS_SSC_DELTA_S 0
-#define RG_LCDDS_SSC_DELTA_M 0xfff
-
-#define CORE_PLL_GROUP11 0x40a
-#define RG_LCDDS_SSC_DELTA1_S 0
-#define RG_LCDDS_SSC_DELTA1_M 0xfff
-
-#define CORE_GSWPLL_GRP1 0x40d
-#define RG_GSWPLL_POSDIV_200M_S 12
-#define RG_GSWPLL_POSDIV_200M_M 0x3000
-#define RG_GSWPLL_EN_PRE BIT(11)
-#define RG_GSWPLL_FBKDIV_200M_S 0
-#define RG_GSWPLL_FBKDIV_200M_M 0xff
-
-#define CORE_GSWPLL_GRP2 0x40e
-#define RG_GSWPLL_POSDIV_500M_S 8
-#define RG_GSWPLL_POSDIV_500M_M 0x300
-#define RG_GSWPLL_FBKDIV_500M_S 0
-#define RG_GSWPLL_FBKDIV_500M_M 0xff
-
-#define CORE_TRGMII_GSW_CLK_CG 0x410
-#define REG_GSWCK_EN BIT(0)
-#define REG_TRGMIICK_EN BIT(1)
-
-/* Extend PHY Control Register 3 */
-#define PHY_EXT_REG_14 0x14
-
-/* Fields of PHY_EXT_REG_14 */
-#define PHY_EN_DOWN_SHFIT BIT(4)
-
-/* Extend PHY Control Register 4 */
-#define PHY_EXT_REG_17 0x17
-
-/* Fields of PHY_EXT_REG_17 */
-#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4)
-
-/* PHY RXADC Control Register 7 */
-#define PHY_DEV1E_REG_0C6 0x0c6
-
-/* Fields of PHY_DEV1E_REG_0C6 */
-#define PHY_POWER_SAVING_S 8
-#define PHY_POWER_SAVING_M 0x300
-#define PHY_POWER_SAVING_TX 0x0
-
/* PDMA descriptors */
struct mtk_rx_dma {
unsigned int rxd1;
@@ -597,4 +413,17 @@ struct mtk_tx_dma_v2 {
#define PDMA_V2_RXD2_PLEN0_GET(_v) FIELD_GET(PDMA_V2_RXD2_PLEN0_M, (_v))
#define PDMA_V2_RXD2_PLEN0_SET(_v) FIELD_PREP(PDMA_V2_RXD2_PLEN0_M, (_v))
+void mtk_fe_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set);
+void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set);
+void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set);
+
+int mtk_mii_read(struct mtk_eth_priv *priv, u8 phy, u8 reg);
+int mtk_mii_write(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 data);
+int mtk_mmd_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg);
+int mtk_mmd_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg,
+ u16 val);
+int mtk_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg);
+int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg,
+ u16 val);
+
#endif /* _MTK_ETH_H_ */
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 13e73810ad6..3132718e4f8 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -2,10 +2,6 @@
config BITBANGMII
bool "Bit-banged ethernet MII management channel support"
-config BITBANGMII_MULTI
- bool "Enable the multi bus support"
- depends on BITBANGMII
-
config MV88E6352_SWITCH
bool "Marvell 88E6352 switch support"
diff --git a/drivers/net/phy/miiphybb.c b/drivers/net/phy/miiphybb.c
index b143137893f..9f5f9b12c9f 100644
--- a/drivers/net/phy/miiphybb.c
+++ b/drivers/net/phy/miiphybb.c
@@ -17,90 +17,6 @@
#include <miiphy.h>
#include <asm/global_data.h>
-#ifndef CONFIG_BITBANGMII_MULTI
-
-/*
- * If CONFIG_BITBANGMII_MULTI is not defined we use a
- * compatibility layer with the previous miiphybb implementation
- * based on macros usage.
- *
- */
-static int bb_mii_init_wrap(struct bb_miiphy_bus *bus)
-{
-#ifdef MII_INIT
- MII_INIT;
-#endif
- return 0;
-}
-
-static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus)
-{
-#ifdef MDIO_DECLARE
- MDIO_DECLARE;
-#endif
- MDIO_ACTIVE;
- return 0;
-}
-
-static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus)
-{
-#ifdef MDIO_DECLARE
- MDIO_DECLARE;
-#endif
- MDIO_TRISTATE;
- return 0;
-}
-
-static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v)
-{
-#ifdef MDIO_DECLARE
- MDIO_DECLARE;
-#endif
- MDIO(v);
- return 0;
-}
-
-static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v)
-{
-#ifdef MDIO_DECLARE
- MDIO_DECLARE;
-#endif
- *v = MDIO_READ;
- return 0;
-}
-
-static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v)
-{
-#ifdef MDC_DECLARE
- MDC_DECLARE;
-#endif
- MDC(v);
- return 0;
-}
-
-static int bb_delay_wrap(struct bb_miiphy_bus *bus)
-{
- MIIDELAY;
- return 0;
-}
-
-struct bb_miiphy_bus bb_miiphy_buses[] = {
- {
- .name = BB_MII_DEVNAME,
- .init = bb_mii_init_wrap,
- .mdio_active = bb_mdio_active_wrap,
- .mdio_tristate = bb_mdio_tristate_wrap,
- .set_mdio = bb_set_mdio_wrap,
- .get_mdio = bb_get_mdio_wrap,
- .set_mdc = bb_set_mdc_wrap,
- .delay = bb_delay_wrap,
- }
-};
-
-int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
- sizeof(bb_miiphy_buses[0]);
-#endif
-
int bb_miiphy_init(void)
{
int i;
@@ -114,7 +30,6 @@ int bb_miiphy_init(void)
static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
{
-#ifdef CONFIG_BITBANGMII_MULTI
int i;
/* Search the correct bus */
@@ -124,10 +39,6 @@ static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
}
}
return NULL;
-#else
- /* We have just one bitbanging bus */
- return &bb_miiphy_buses[0];
-#endif
}
/*****************************************************************************
@@ -272,9 +183,7 @@ int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
bus->set_mdc(bus, 1);
bus->delay(bus);
-#ifdef DEBUG
- printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
-#endif
+ debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
return rdreg;
}
diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c
index a0878193ac0..850c0cbec96 100644
--- a/drivers/net/phy/ti_phy_init.c
+++ b/drivers/net/phy/ti_phy_init.c
@@ -10,12 +10,58 @@
#include <phy.h>
#include "ti_phy_init.h"
+#define DP83822_DEVADDR 0x1f
+
+#define MII_DP83822_RCSR 0x17
+
+/* RCSR bits */
+#define DP83822_RX_CLK_SHIFT BIT(12)
+#define DP83822_TX_CLK_SHIFT BIT(11)
+
+/* DP83822 specific RGMII RX/TX delay configuration. */
+static int dp83822_config(struct phy_device *phydev)
+{
+ ofnode node = phy_get_ofnode(phydev);
+ u32 rgmii_delay = 0;
+ u32 rx_delay = 0;
+ u32 tx_delay = 0;
+ int ret;
+
+ ret = ofnode_read_u32(node, "rx-internal-delay-ps", &rx_delay);
+ if (ret) {
+ rx_delay = phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID;
+ }
+
+ ret = ofnode_read_u32(node, "tx-internal-delay-ps", &tx_delay);
+ if (ret) {
+ tx_delay = phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID;
+ }
+
+ /* Bit set means Receive path internal clock shift is ENABLED */
+ if (rx_delay)
+ rgmii_delay |= DP83822_RX_CLK_SHIFT;
+
+ /* Bit set means Transmit path internal clock shift is DISABLED */
+ if (!tx_delay)
+ rgmii_delay |= DP83822_TX_CLK_SHIFT;
+
+ ret = phy_modify_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RCSR,
+ DP83822_RX_CLK_SHIFT | DP83822_TX_CLK_SHIFT,
+ rgmii_delay);
+ if (ret)
+ return ret;
+
+ return genphy_config_aneg(phydev);
+}
+
U_BOOT_PHY_DRIVER(dp83822) = {
.name = "TI DP83822",
.uid = 0x2000a240,
.mask = 0xfffffff0,
.features = PHY_BASIC_FEATURES,
- .config = &genphy_config_aneg,
+ .config = &dp83822_config,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
};
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c
index 231764e60b5..7286ad19598 100644
--- a/drivers/net/ravb.c
+++ b/drivers/net/ravb.c
@@ -560,12 +560,12 @@ static int ravb_remove(struct udevice *dev)
return 0;
}
-int ravb_bb_init(struct bb_miiphy_bus *bus)
+static int ravb_bb_init(struct bb_miiphy_bus *bus)
{
return 0;
}
-int ravb_bb_mdio_active(struct bb_miiphy_bus *bus)
+static int ravb_bb_mdio_active(struct bb_miiphy_bus *bus)
{
struct ravb_priv *eth = bus->priv;
@@ -574,7 +574,7 @@ int ravb_bb_mdio_active(struct bb_miiphy_bus *bus)
return 0;
}
-int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus)
+static int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus)
{
struct ravb_priv *eth = bus->priv;
@@ -583,7 +583,7 @@ int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus)
return 0;
}
-int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
+static int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
{
struct ravb_priv *eth = bus->priv;
@@ -595,7 +595,7 @@ int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
return 0;
}
-int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
+static int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
{
struct ravb_priv *eth = bus->priv;
@@ -604,7 +604,7 @@ int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
return 0;
}
-int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
+static int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
{
struct ravb_priv *eth = bus->priv;
@@ -616,7 +616,7 @@ int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
return 0;
}
-int ravb_bb_delay(struct bb_miiphy_bus *bus)
+static int ravb_bb_delay(struct bb_miiphy_bus *bus)
{
udelay(10);