Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 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)); } } |