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