1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2026 David Yang 4 */ 5 6 #include "chip.h" 7 #include "mdio_bus.h" 8 #include "pcs.h" 9 #include "smi.h" 10 11 static int 12 yt921x_serdes_config(struct yt921x_priv *priv, int port, unsigned int neg_mode, 13 phy_interface_t interface, 14 const unsigned long *advertising, bool permit_pause_to_mac) 15 { 16 bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND; 17 struct yt921x_port *pp = &priv->ports[port]; 18 bool changed = false; 19 u16 bmcr; 20 u32 mask; 21 u32 ctrl; 22 u16 val; 23 int adv; 24 int res; 25 26 switch (interface) { 27 case PHY_INTERFACE_MODE_SGMII: 28 ctrl = YT921X_SERDES_MODE_SGMII; 29 break; 30 case PHY_INTERFACE_MODE_100BASEX: 31 ctrl = YT921X_SERDES_MODE_100BASEX; 32 break; 33 case PHY_INTERFACE_MODE_1000BASEX: 34 ctrl = YT921X_SERDES_MODE_1000BASEX; 35 break; 36 case PHY_INTERFACE_MODE_2500BASEX: 37 ctrl = YT921X_SERDES_MODE_2500BASEX; 38 break; 39 default: 40 return -EOPNOTSUPP; 41 } 42 43 res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), 44 YT921X_SERDES_MODE_M, ctrl); 45 if (res) 46 return res; 47 48 mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_CTRL_PAUSE_AN; 49 ctrl = 0; 50 if (inband) 51 ctrl |= YT921X_PORT_CTRL_LINK_AN; 52 if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac && 53 interface != PHY_INTERFACE_MODE_SGMII) 54 ctrl |= YT921X_PORT_CTRL_PAUSE_AN; 55 res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); 56 if (res) 57 return res; 58 59 if (inband) { 60 res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), 61 YT921X_SERDES_LINK); 62 if (res) 63 return res; 64 } 65 66 adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); 67 if (adv >= 0) { 68 res = yt921x_intif_modify_changed(priv, port, MII_ADVERTISE, 69 U16_MAX, adv); 70 if (res < 0) 71 return res; 72 73 changed = !!res; 74 } 75 76 res = yt921x_intif_read(priv, port, MII_BMCR, &val); 77 if (res) 78 return res; 79 80 bmcr = val; 81 if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 82 bmcr |= BMCR_ANENABLE; 83 else 84 bmcr &= ~BMCR_ANENABLE; 85 86 /* If the ANENABLE bit was changed, the PHY will restart negotiation, 87 * so we don't need to flag a change to trigger its own restart. 88 */ 89 if (bmcr != val) 90 changed = false; 91 92 bmcr &= ~BMCR_ISOLATE; 93 res = yt921x_intif_write(priv, port, MII_BMCR, bmcr); 94 if (res) 95 return res; 96 97 pp->inband = inband; 98 return changed; 99 } 100 101 static unsigned int 102 yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs, 103 phy_interface_t interface) 104 { 105 if (interface == PHY_INTERFACE_MODE_100BASEX) 106 return LINK_INBAND_DISABLE; 107 108 return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; 109 } 110 111 static void 112 yt921x_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, 113 struct phylink_link_state *state) 114 { 115 struct yt921x_port *pp = pcs_to_yt921x_port(pcs); 116 struct yt921x_priv *priv = yt921x_port_to_priv(pp); 117 struct device *dev = yt921x_priv_to_device(priv); 118 int port = pp->index; 119 u16 bmsr; 120 u16 lpa; 121 int res; 122 123 mutex_lock(&priv->reg_lock); 124 125 res = yt921x_intif_read(priv, port, MII_BMSR, &bmsr); 126 if (res) 127 goto end; 128 129 res = yt921x_intif_read(priv, port, MII_LPA, &lpa); 130 131 end: 132 mutex_unlock(&priv->reg_lock); 133 134 if (res) { 135 dev_err_ratelimited(dev, "Failed to %s PCS port %d: %i\n", 136 "get state of", port, res); 137 state->link = false; 138 return; 139 } 140 141 if (state->interface == PHY_INTERFACE_MODE_100BASEX) { 142 state->speed = SPEED_100; 143 state->duplex = DUPLEX_FULL; 144 state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; 145 state->link = !!(bmsr & BMSR_LSTATUS); 146 return; 147 } 148 149 phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa); 150 } 151 152 static void yt921x_phylink_pcs_an_restart(struct phylink_pcs *pcs) 153 { 154 struct yt921x_port *pp = pcs_to_yt921x_port(pcs); 155 struct yt921x_priv *priv = yt921x_port_to_priv(pp); 156 struct device *dev = yt921x_priv_to_device(priv); 157 int port = pp->index; 158 u16 val; 159 int res; 160 161 mutex_lock(&priv->reg_lock); 162 163 res = yt921x_intif_read(priv, port, MII_BMCR, &val); 164 if (res) 165 goto end; 166 167 val |= BMCR_ANRESTART; 168 res = yt921x_intif_write(priv, port, MII_BMCR, val); 169 170 end: 171 mutex_unlock(&priv->reg_lock); 172 173 if (res) 174 dev_err(dev, "Failed to %s PCS port %d: %i\n", "restart", 175 port, res); 176 } 177 178 static void yt921x_phylink_pcs_disable(struct phylink_pcs *pcs) 179 { 180 struct yt921x_port *pp = pcs_to_yt921x_port(pcs); 181 struct yt921x_priv *priv = yt921x_port_to_priv(pp); 182 struct device *dev = yt921x_priv_to_device(priv); 183 int port = pp->index; 184 int res; 185 186 mutex_lock(&priv->reg_lock); 187 res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); 188 mutex_unlock(&priv->reg_lock); 189 190 if (res) 191 dev_err(dev, "Failed to %s PCS port %d: %i\n", "power down", 192 port, res); 193 } 194 195 static int yt921x_phylink_pcs_enable(struct phylink_pcs *pcs) 196 { 197 struct yt921x_port *pp = pcs_to_yt921x_port(pcs); 198 struct yt921x_priv *priv = yt921x_port_to_priv(pp); 199 int port = pp->index; 200 u16 val; 201 int res; 202 203 mutex_lock(&priv->reg_lock); 204 205 res = yt921x_intif_read(priv, port, MII_BMCR, &val); 206 if (res) 207 goto end; 208 209 val &= ~BMCR_PDOWN; 210 val |= BMCR_ANRESTART; 211 res = yt921x_intif_write(priv, port, MII_BMCR, val); 212 213 end: 214 mutex_unlock(&priv->reg_lock); 215 216 return res; 217 } 218 219 static int 220 yt921x_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 221 phy_interface_t interface, 222 const unsigned long *advertising, 223 bool permit_pause_to_mac) 224 { 225 struct yt921x_port *pp = pcs_to_yt921x_port(pcs); 226 struct yt921x_priv *priv = yt921x_port_to_priv(pp); 227 int port = pp->index; 228 int res; 229 230 mutex_lock(&priv->reg_lock); 231 res = yt921x_serdes_config(priv, port, neg_mode, interface, 232 advertising, permit_pause_to_mac); 233 mutex_unlock(&priv->reg_lock); 234 235 return res; 236 } 237 238 const struct phylink_pcs_ops yt921x_phylink_pcs_ops = { 239 .pcs_inband_caps = yt921x_phylink_pcs_inband_caps, 240 .pcs_get_state = yt921x_phylink_pcs_get_state, 241 .pcs_an_restart = yt921x_phylink_pcs_an_restart, 242 .pcs_disable = yt921x_phylink_pcs_disable, 243 .pcs_enable = yt921x_phylink_pcs_enable, 244 .pcs_config = yt921x_phylink_pcs_config, 245 }; 246