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>
294 lines
7.7 KiB
C
294 lines
7.7 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 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,
|
|
};
|