developer | 29d9d9f | 2025-01-10 16:41:13 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright (C) 2025 MediaTek Inc. |
| 4 | * |
| 5 | * Author: Weijie Gao <weijie.gao@mediatek.com> |
| 6 | * Author: Mark Lee <mark-mc.lee@mediatek.com> |
| 7 | */ |
| 8 | |
| 9 | #include <miiphy.h> |
| 10 | #include <linux/delay.h> |
| 11 | #include <linux/mdio.h> |
| 12 | #include <linux/mii.h> |
| 13 | #include <linux/io.h> |
| 14 | #include "mtk_eth.h" |
| 15 | #include "mt753x.h" |
| 16 | |
| 17 | static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data) |
| 18 | { |
| 19 | *data = readl(priv->epriv.ethsys_base + GSW_BASE + reg); |
| 20 | |
| 21 | return 0; |
| 22 | } |
| 23 | |
| 24 | static int mt7988_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data) |
| 25 | { |
| 26 | writel(data, priv->epriv.ethsys_base + GSW_BASE + reg); |
| 27 | |
| 28 | return 0; |
| 29 | } |
| 30 | |
| 31 | static void mt7988_phy_setting(struct mt753x_switch_priv *priv) |
| 32 | { |
| 33 | u16 val; |
| 34 | u32 i; |
| 35 | |
| 36 | for (i = 0; i < MT753X_NUM_PHYS; i++) { |
| 37 | /* Enable HW auto downshift */ |
| 38 | mt7531_mii_write(priv, i, 0x1f, 0x1); |
| 39 | val = mt7531_mii_read(priv, i, PHY_EXT_REG_14); |
| 40 | val |= PHY_EN_DOWN_SHFIT; |
| 41 | mt7531_mii_write(priv, i, PHY_EXT_REG_14, val); |
| 42 | |
| 43 | /* PHY link down power saving enable */ |
| 44 | val = mt7531_mii_read(priv, i, PHY_EXT_REG_17); |
| 45 | val |= PHY_LINKDOWN_POWER_SAVING_EN; |
| 46 | mt7531_mii_write(priv, i, PHY_EXT_REG_17, val); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | static void mt7988_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) |
| 51 | { |
| 52 | struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; |
| 53 | u32 pmcr = FORCE_MODE_LNK; |
| 54 | |
| 55 | if (enable) |
| 56 | pmcr = priv->pmcr; |
| 57 | |
| 58 | mt7988_reg_write(priv, PMCR_REG(6), pmcr); |
| 59 | } |
| 60 | |
| 61 | static int mt7988_setup(struct mtk_eth_switch_priv *swpriv) |
| 62 | { |
| 63 | struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; |
| 64 | u16 phy_addr, phy_val; |
| 65 | u32 pmcr; |
| 66 | int i; |
| 67 | |
| 68 | priv->smi_addr = MT753X_DFL_SMI_ADDR; |
| 69 | priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK; |
| 70 | priv->reg_read = mt7988_reg_read; |
| 71 | priv->reg_write = mt7988_reg_write; |
| 72 | |
| 73 | /* Turn off PHYs */ |
| 74 | for (i = 0; i < MT753X_NUM_PHYS; i++) { |
| 75 | phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); |
| 76 | phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); |
| 77 | phy_val |= BMCR_PDOWN; |
| 78 | mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); |
| 79 | } |
| 80 | |
| 81 | switch (priv->epriv.phy_interface) { |
| 82 | case PHY_INTERFACE_MODE_USXGMII: |
| 83 | /* Use CPU bridge instead of actual USXGMII path */ |
| 84 | |
| 85 | /* Disable GDM1 RX CRC stripping */ |
| 86 | /* mtk_fe_rmw(priv, 0x500, BIT(16), 0); */ |
| 87 | |
| 88 | /* Set GDM1 no drop */ |
| 89 | mtk_fe_rmw(priv->epriv.eth, PSE_NO_DROP_CFG_REG, 0, |
| 90 | PSE_NO_DROP_GDM1); |
| 91 | |
| 92 | /* Enable GSW CPU bridge as USXGMII */ |
| 93 | /* mtk_fe_rmw(priv, 0x504, BIT(31), BIT(31)); */ |
| 94 | |
| 95 | /* Enable GDM1 to GSW CPU bridge */ |
| 96 | mtk_gmac_rmw(priv->epriv.eth, GMAC_MAC_MISC_REG, 0, BIT(0)); |
| 97 | |
| 98 | /* XGMAC force link up */ |
| 99 | mtk_gmac_rmw(priv->epriv.eth, GMAC_XGMAC_STS_REG, 0, |
| 100 | P1_XGMAC_FORCE_LINK); |
| 101 | |
| 102 | /* Setup GSW CPU bridge IPG */ |
| 103 | mtk_gmac_rmw(priv->epriv.eth, GMAC_GSW_CFG_REG, |
| 104 | GSWTX_IPG_M | GSWRX_IPG_M, |
| 105 | (0xB << GSWTX_IPG_S) | (0xB << GSWRX_IPG_S)); |
| 106 | break; |
| 107 | default: |
| 108 | printf("Error: MT7988 GSW does not support %s interface\n", |
| 109 | phy_string_for_interface(priv->epriv.phy_interface)); |
| 110 | break; |
| 111 | } |
| 112 | |
| 113 | pmcr = MT7988_FORCE_MODE | |
| 114 | (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | |
| 115 | MAC_MODE | MAC_TX_EN | MAC_RX_EN | |
| 116 | BKOFF_EN | BACKPR_EN | |
| 117 | FORCE_RX_FC | FORCE_TX_FC | |
| 118 | (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX | |
| 119 | FORCE_LINK; |
| 120 | |
| 121 | priv->pmcr = pmcr; |
| 122 | |
| 123 | /* Keep MAC link down before starting eth */ |
| 124 | mt7988_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK); |
| 125 | |
| 126 | /* Enable port isolation to block inter-port communication */ |
| 127 | mt753x_port_isolation(priv); |
| 128 | |
| 129 | /* Turn on PHYs */ |
| 130 | for (i = 0; i < MT753X_NUM_PHYS; i++) { |
| 131 | phy_addr = MT753X_PHY_ADDR(priv->phy_base, i); |
| 132 | phy_val = mt7531_mii_read(priv, phy_addr, MII_BMCR); |
| 133 | phy_val &= ~BMCR_PDOWN; |
| 134 | mt7531_mii_write(priv, phy_addr, MII_BMCR, phy_val); |
| 135 | } |
| 136 | |
| 137 | mt7988_phy_setting(priv); |
| 138 | |
| 139 | return mt7531_mdio_register(priv); |
| 140 | } |
| 141 | |
| 142 | static int mt7531_cleanup(struct mtk_eth_switch_priv *swpriv) |
| 143 | { |
| 144 | struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv; |
| 145 | |
| 146 | mdio_unregister(priv->mdio_bus); |
| 147 | |
| 148 | return 0; |
| 149 | } |
| 150 | |
| 151 | MTK_ETH_SWITCH(mt7988) = { |
| 152 | .name = "mt7988", |
| 153 | .desc = "MediaTek MT7988 built-in switch", |
| 154 | .priv_size = sizeof(struct mt753x_switch_priv), |
| 155 | .reset_wait_time = 50, |
| 156 | |
| 157 | .setup = mt7988_setup, |
| 158 | .cleanup = mt7531_cleanup, |
| 159 | .mac_control = mt7988_mac_control, |
| 160 | }; |