// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 MediaTek Inc. * * Author: Weijie Gao * Author: Mark Lee */ #include #include #include #include #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, };