1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 2 /* Copyright 2020 NXP 3 * Lynx PCS MDIO helpers 4 */ 5 6 #include <linux/mdio.h> 7 #include <linux/phylink.h> 8 #include <linux/pcs-lynx.h> 9 10 #define SGMII_CLOCK_PERIOD_NS 8 /* PCS is clocked at 125 MHz */ 11 #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS)) 12 13 #define LINK_TIMER_LO 0x12 14 #define LINK_TIMER_HI 0x13 15 #define IF_MODE 0x14 16 #define IF_MODE_SGMII_EN BIT(0) 17 #define IF_MODE_USE_SGMII_AN BIT(1) 18 #define IF_MODE_SPEED(x) (((x) << 2) & GENMASK(3, 2)) 19 #define IF_MODE_SPEED_MSK GENMASK(3, 2) 20 #define IF_MODE_HALF_DUPLEX BIT(4) 21 22 struct lynx_pcs { 23 struct phylink_pcs pcs; 24 struct mdio_device *mdio; 25 }; 26 27 enum sgmii_speed { 28 SGMII_SPEED_10 = 0, 29 SGMII_SPEED_100 = 1, 30 SGMII_SPEED_1000 = 2, 31 SGMII_SPEED_2500 = 2, 32 }; 33 34 #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs) 35 #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs) 36 37 struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs) 38 { 39 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 40 41 return lynx->mdio; 42 } 43 EXPORT_SYMBOL(lynx_get_mdio_device); 44 45 static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs, 46 struct phylink_link_state *state) 47 { 48 struct mii_bus *bus = pcs->bus; 49 int addr = pcs->addr; 50 int status, lpa; 51 52 status = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_BMSR); 53 if (status < 0) 54 return; 55 56 state->link = !!(status & MDIO_STAT1_LSTATUS); 57 state->an_complete = !!(status & MDIO_AN_STAT1_COMPLETE); 58 if (!state->link || !state->an_complete) 59 return; 60 61 lpa = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_LPA); 62 if (lpa < 0) 63 return; 64 65 phylink_decode_usxgmii_word(state, lpa); 66 } 67 68 static void lynx_pcs_get_state_2500basex(struct mdio_device *pcs, 69 struct phylink_link_state *state) 70 { 71 int bmsr, lpa; 72 73 bmsr = mdiodev_read(pcs, MII_BMSR); 74 lpa = mdiodev_read(pcs, MII_LPA); 75 if (bmsr < 0 || lpa < 0) { 76 state->link = false; 77 return; 78 } 79 80 state->link = !!(bmsr & BMSR_LSTATUS); 81 state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); 82 if (!state->link) 83 return; 84 85 state->speed = SPEED_2500; 86 state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; 87 state->duplex = DUPLEX_FULL; 88 } 89 90 static void lynx_pcs_get_state(struct phylink_pcs *pcs, 91 struct phylink_link_state *state) 92 { 93 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 94 95 switch (state->interface) { 96 case PHY_INTERFACE_MODE_1000BASEX: 97 case PHY_INTERFACE_MODE_SGMII: 98 case PHY_INTERFACE_MODE_QSGMII: 99 phylink_mii_c22_pcs_get_state(lynx->mdio, state); 100 break; 101 case PHY_INTERFACE_MODE_2500BASEX: 102 lynx_pcs_get_state_2500basex(lynx->mdio, state); 103 break; 104 case PHY_INTERFACE_MODE_USXGMII: 105 lynx_pcs_get_state_usxgmii(lynx->mdio, state); 106 break; 107 case PHY_INTERFACE_MODE_10GBASER: 108 phylink_mii_c45_pcs_get_state(lynx->mdio, state); 109 break; 110 default: 111 break; 112 } 113 114 dev_dbg(&lynx->mdio->dev, 115 "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n", 116 phy_modes(state->interface), 117 phy_speed_to_str(state->speed), 118 phy_duplex_to_str(state->duplex), 119 state->link, state->an_enabled, state->an_complete); 120 } 121 122 static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, 123 phy_interface_t interface, 124 const unsigned long *advertising) 125 { 126 int link_timer_ns; 127 u32 link_timer; 128 u16 if_mode; 129 int err; 130 131 link_timer_ns = phylink_get_link_timer_ns(interface); 132 if (link_timer_ns > 0) { 133 link_timer = LINK_TIMER_VAL(link_timer_ns); 134 135 mdiodev_write(pcs, LINK_TIMER_LO, link_timer & 0xffff); 136 mdiodev_write(pcs, LINK_TIMER_HI, link_timer >> 16); 137 } 138 139 if (interface == PHY_INTERFACE_MODE_1000BASEX) { 140 if_mode = 0; 141 } else { 142 if_mode = IF_MODE_SGMII_EN; 143 if (mode == MLO_AN_INBAND) 144 if_mode |= IF_MODE_USE_SGMII_AN; 145 } 146 147 err = mdiodev_modify(pcs, IF_MODE, 148 IF_MODE_SGMII_EN | IF_MODE_USE_SGMII_AN, 149 if_mode); 150 if (err) 151 return err; 152 153 return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising); 154 } 155 156 static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode, 157 const unsigned long *advertising) 158 { 159 struct mii_bus *bus = pcs->bus; 160 int addr = pcs->addr; 161 162 if (!phylink_autoneg_inband(mode)) { 163 dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n"); 164 return -EOPNOTSUPP; 165 } 166 167 /* Configure device ability for the USXGMII Replicator */ 168 return mdiobus_c45_write(bus, addr, MDIO_MMD_VEND2, MII_ADVERTISE, 169 MDIO_USXGMII_10G | MDIO_USXGMII_LINK | 170 MDIO_USXGMII_FULL_DUPLEX | 171 ADVERTISE_SGMII | ADVERTISE_LPACK); 172 } 173 174 static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode, 175 phy_interface_t ifmode, 176 const unsigned long *advertising, 177 bool permit) 178 { 179 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 180 181 switch (ifmode) { 182 case PHY_INTERFACE_MODE_1000BASEX: 183 case PHY_INTERFACE_MODE_SGMII: 184 case PHY_INTERFACE_MODE_QSGMII: 185 return lynx_pcs_config_giga(lynx->mdio, mode, ifmode, 186 advertising); 187 case PHY_INTERFACE_MODE_2500BASEX: 188 if (phylink_autoneg_inband(mode)) { 189 dev_err(&lynx->mdio->dev, 190 "AN not supported on 3.125GHz SerDes lane\n"); 191 return -EOPNOTSUPP; 192 } 193 break; 194 case PHY_INTERFACE_MODE_USXGMII: 195 return lynx_pcs_config_usxgmii(lynx->mdio, mode, advertising); 196 case PHY_INTERFACE_MODE_10GBASER: 197 /* Nothing to do here for 10GBASER */ 198 break; 199 default: 200 return -EOPNOTSUPP; 201 } 202 203 return 0; 204 } 205 206 static void lynx_pcs_an_restart(struct phylink_pcs *pcs) 207 { 208 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 209 210 phylink_mii_c22_pcs_an_restart(lynx->mdio); 211 } 212 213 static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode, 214 int speed, int duplex) 215 { 216 u16 if_mode = 0, sgmii_speed; 217 218 /* The PCS needs to be configured manually only 219 * when not operating on in-band mode 220 */ 221 if (mode == MLO_AN_INBAND) 222 return; 223 224 if (duplex == DUPLEX_HALF) 225 if_mode |= IF_MODE_HALF_DUPLEX; 226 227 switch (speed) { 228 case SPEED_1000: 229 sgmii_speed = SGMII_SPEED_1000; 230 break; 231 case SPEED_100: 232 sgmii_speed = SGMII_SPEED_100; 233 break; 234 case SPEED_10: 235 sgmii_speed = SGMII_SPEED_10; 236 break; 237 case SPEED_UNKNOWN: 238 /* Silently don't do anything */ 239 return; 240 default: 241 dev_err(&pcs->dev, "Invalid PCS speed %d\n", speed); 242 return; 243 } 244 if_mode |= IF_MODE_SPEED(sgmii_speed); 245 246 mdiodev_modify(pcs, IF_MODE, 247 IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK, 248 if_mode); 249 } 250 251 /* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane 252 * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have 253 * auto-negotiation of any link parameters. Electrically it is compatible with 254 * a single lane of XAUI. 255 * The hardware reference manual wants to call this mode SGMII, but it isn't 256 * really, since the fundamental features of SGMII: 257 * - Downgrading the link speed by duplicating symbols 258 * - Auto-negotiation 259 * are not there. 260 * The speed is configured at 1000 in the IF_MODE because the clock frequency 261 * is actually given by a PLL configured in the Reset Configuration Word (RCW). 262 * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o 263 * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a 264 * lower link speed on line side, the system-side interface remains fixed at 265 * 2500 Mbps and we do rate adaptation through pause frames. 266 */ 267 static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs, 268 unsigned int mode, 269 int speed, int duplex) 270 { 271 u16 if_mode = 0; 272 273 if (mode == MLO_AN_INBAND) { 274 dev_err(&pcs->dev, "AN not supported for 2500BaseX\n"); 275 return; 276 } 277 278 if (duplex == DUPLEX_HALF) 279 if_mode |= IF_MODE_HALF_DUPLEX; 280 if_mode |= IF_MODE_SPEED(SGMII_SPEED_2500); 281 282 mdiodev_modify(pcs, IF_MODE, 283 IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK, 284 if_mode); 285 } 286 287 static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, 288 phy_interface_t interface, 289 int speed, int duplex) 290 { 291 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 292 293 switch (interface) { 294 case PHY_INTERFACE_MODE_SGMII: 295 case PHY_INTERFACE_MODE_QSGMII: 296 lynx_pcs_link_up_sgmii(lynx->mdio, mode, speed, duplex); 297 break; 298 case PHY_INTERFACE_MODE_2500BASEX: 299 lynx_pcs_link_up_2500basex(lynx->mdio, mode, speed, duplex); 300 break; 301 case PHY_INTERFACE_MODE_USXGMII: 302 /* At the moment, only in-band AN is supported for USXGMII 303 * so nothing to do in link_up 304 */ 305 break; 306 default: 307 break; 308 } 309 } 310 311 static const struct phylink_pcs_ops lynx_pcs_phylink_ops = { 312 .pcs_get_state = lynx_pcs_get_state, 313 .pcs_config = lynx_pcs_config, 314 .pcs_an_restart = lynx_pcs_an_restart, 315 .pcs_link_up = lynx_pcs_link_up, 316 }; 317 318 struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio) 319 { 320 struct lynx_pcs *lynx; 321 322 lynx = kzalloc(sizeof(*lynx), GFP_KERNEL); 323 if (!lynx) 324 return NULL; 325 326 lynx->mdio = mdio; 327 lynx->pcs.ops = &lynx_pcs_phylink_ops; 328 lynx->pcs.poll = true; 329 330 return lynx_to_phylink_pcs(lynx); 331 } 332 EXPORT_SYMBOL(lynx_pcs_create); 333 334 void lynx_pcs_destroy(struct phylink_pcs *pcs) 335 { 336 struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); 337 338 kfree(lynx); 339 } 340 EXPORT_SYMBOL(lynx_pcs_destroy); 341 342 MODULE_LICENSE("Dual BSD/GPL"); 343