mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-13 18:56:59 +02:00
mtk_eth.c contains not only the ethernet GMAC/DMA driver, but also some ethernet switch initialization code. As we may add more switch support in the future, it's better to move them out of mtk_eth.c to avoid increasing the code complexity. Since not all switches are supported for a particular board, Kconfig options are added to allow user to select which switch should be built into u-boot. If multiple switches are selected, auto-detecting can also be enabled. Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
282 lines
7.3 KiB
C
282 lines
7.3 KiB
C
// 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,
|
|
};
|