1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Phylink and PCS support for MaxLinear MxL862xx switch family 4 * 5 * Copyright (C) 2024 MaxLinear Inc. 6 * Copyright (C) 2025 John Crispin <john@phrozen.org> 7 * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 8 */ 9 10 #include <linux/bitfield.h> 11 #include <linux/mutex.h> 12 #include <linux/phylink.h> 13 #include <net/dsa.h> 14 15 #include "mxl862xx.h" 16 #include "mxl862xx-api.h" 17 #include "mxl862xx-cmd.h" 18 #include "mxl862xx-host.h" 19 #include "mxl862xx-phylink.h" 20 21 void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, 22 struct phylink_config *config) 23 { 24 struct mxl862xx_priv *priv = ds->priv; 25 26 config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | 27 MAC_100 | MAC_1000 | MAC_2500FD; 28 29 switch (port) { 30 case 1 ... 8: 31 __set_bit(PHY_INTERFACE_MODE_INTERNAL, 32 config->supported_interfaces); 33 break; 34 case 9: 35 case 13: 36 /* Advertised also on old firmware lacking the XPCS API: 37 * there the SerDes runs in its flash-configured mode 38 * without host control (mac_select_pcs returns NULL), 39 * keeping the CPU port working. 40 */ 41 __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); 42 __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces); 43 __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces); 44 __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces); 45 __set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces); 46 __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces); 47 fallthrough; 48 case 10 ... 12: 49 case 14 ... 16: 50 if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) 51 break; 52 __set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces); 53 __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces); 54 55 break; 56 default: 57 break; 58 } 59 60 if (port == 9 || port == 13) 61 config->mac_capabilities |= MAC_10000FD | MAC_5000FD; 62 } 63 64 static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs) 65 { 66 return container_of(pcs, struct mxl862xx_pcs, pcs); 67 } 68 69 static int mxl862xx_xpcs_if_mode(phy_interface_t interface) 70 { 71 switch (interface) { 72 case PHY_INTERFACE_MODE_SGMII: 73 return MXL862XX_XPCS_IF_SGMII; 74 case PHY_INTERFACE_MODE_QSGMII: 75 return MXL862XX_XPCS_IF_QSGMII; 76 case PHY_INTERFACE_MODE_1000BASEX: 77 return MXL862XX_XPCS_IF_1000BASEX; 78 case PHY_INTERFACE_MODE_2500BASEX: 79 return MXL862XX_XPCS_IF_2500BASEX; 80 case PHY_INTERFACE_MODE_USXGMII: 81 case PHY_INTERFACE_MODE_10G_QXGMII: 82 return MXL862XX_XPCS_IF_USXGMII; 83 case PHY_INTERFACE_MODE_10GBASER: 84 return MXL862XX_XPCS_IF_10GBASER; 85 case PHY_INTERFACE_MODE_10GKR: 86 return MXL862XX_XPCS_IF_10GKR; 87 default: 88 return -EINVAL; 89 } 90 } 91 92 static int mxl862xx_xpcs_neg_mode(unsigned int neg_mode) 93 { 94 if (!(neg_mode & PHYLINK_PCS_NEG_INBAND)) 95 return MXL862XX_XPCS_NEG_NONE; 96 if (neg_mode & PHYLINK_PCS_NEG_ENABLED) 97 return MXL862XX_XPCS_NEG_INBAND_AN_ON; 98 return MXL862XX_XPCS_NEG_INBAND_AN_OFF; 99 } 100 101 static int mxl862xx_pcs_enable(struct phylink_pcs *pcs) 102 { 103 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 104 105 /* Bringup is done idempotently by pcs_config; just account this 106 * sub-port so pcs_disable powers the shared XPCS down only after 107 * the last sub-port has been released. 108 */ 109 mutex_lock(&mpcs->priv->serdes_lock); 110 mpcs->priv->serdes_refcount[mpcs->serdes_id]++; 111 mutex_unlock(&mpcs->priv->serdes_lock); 112 113 return 0; 114 } 115 116 static void mxl862xx_pcs_disable(struct phylink_pcs *pcs) 117 { 118 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 119 struct mxl862xx_xpcs_pcs_disable dis = {}; 120 struct mxl862xx_priv *priv = mpcs->priv; 121 122 dis.port_id = mpcs->serdes_id; 123 124 /* The SerDes is shared across QSGMII/QUSXGMII sub-ports; only 125 * power it down once the last active sub-port goes away. Hold 126 * serdes_lock across the count and the power-down so a sibling 127 * sub-port enable cannot race the transition to zero. 128 */ 129 mutex_lock(&priv->serdes_lock); 130 if (--priv->serdes_refcount[mpcs->serdes_id] == 0) 131 MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, dis); 132 mutex_unlock(&priv->serdes_lock); 133 } 134 135 /* The XPCS firmware reports failures in the result field using its own 136 * libc errno values; ENOTSUP (134) in particular has no kernel errno. 137 * Translate the codes the firmware can actually return. 138 */ 139 static int mxl862xx_xpcs_errno(int result) 140 { 141 switch (result) { 142 case -5: /* firmware -EIO */ 143 return -EIO; 144 case -134: /* firmware -ENOTSUP */ 145 return -EOPNOTSUPP; 146 default: /* firmware -EINVAL and anything unexpected */ 147 return -EINVAL; 148 } 149 } 150 151 static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 152 phy_interface_t interface, 153 const unsigned long *advertising, 154 bool permit_pause_to_mac) 155 { 156 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 157 struct mxl862xx_priv *priv = mpcs->priv; 158 struct mxl862xx_xpcs_pcs_cfg cfg = {}; 159 int if_mode, lane, ret, adv; 160 161 if_mode = mxl862xx_xpcs_if_mode(interface); 162 if (if_mode < 0) { 163 dev_err(priv->ds->dev, "unsupported interface: %s\n", 164 phy_modes(interface)); 165 return if_mode; 166 } 167 168 /* The XPCS bringup is per-instance and idempotent in the 169 * firmware: every QSGMII/QUSXGMII sub-port may call pcs_config 170 * and the firmware will skip the bringup if the requested mode 171 * matches the cached one, then update MAC pause for the 172 * sub-port indicated by @usx_subport. No serdes_lock is needed 173 * here: the refcount held since pcs_enable keeps a sibling 174 * pcs_disable from powering the XPCS down, and pcs_disable 175 * invalidates the firmware's cached mode so the next pcs_config 176 * redoes the bringup. 177 */ 178 lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ? 179 MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; 180 181 cfg.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_CFG_PORT_ID, 182 mpcs->serdes_id) | 183 FIELD_PREP(MXL862XX_XPCS_CFG_USX_SUBPORT, 184 mpcs->slot) | 185 FIELD_PREP(MXL862XX_XPCS_CFG_USX_LANE_MODE, lane) | 186 FIELD_PREP(MXL862XX_XPCS_CFG_INTERFACE, if_mode) | 187 FIELD_PREP(MXL862XX_XPCS_CFG_NEG_MODE, 188 mxl862xx_xpcs_neg_mode(neg_mode)) | 189 FIELD_PREP(MXL862XX_XPCS_CFG_ROLE, 190 MXL862XX_XPCS_ROLE_MAC) | 191 FIELD_PREP(MXL862XX_XPCS_CFG_PERMIT_PAUSE, 192 permit_pause_to_mac)); 193 194 if (neg_mode & PHYLINK_PCS_NEG_INBAND) { 195 adv = phylink_mii_c22_pcs_encode_advertisement(interface, 196 advertising); 197 if (adv >= 0) 198 cfg.advertising.cl37 = cpu_to_le16(adv); 199 } 200 201 ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_CONFIG, cfg); 202 if (ret) 203 return ret; 204 205 ret = (s16)le16_to_cpu(cfg.result); 206 if (ret < 0) 207 return mxl862xx_xpcs_errno(ret); 208 209 mpcs->interface = interface; 210 return ret > 0 ? 1 : 0; 211 } 212 213 static void mxl862xx_pcs_get_state(struct phylink_pcs *pcs, 214 unsigned int neg_mode, 215 struct phylink_link_state *state) 216 { 217 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 218 struct mxl862xx_priv *priv = mpcs->priv; 219 struct mxl862xx_xpcs_pcs_state st = {}; 220 int if_mode, lane, ret; 221 u32 mode; 222 u16 bmsr; 223 224 if_mode = mxl862xx_xpcs_if_mode(state->interface); 225 if (if_mode < 0) { 226 state->link = false; 227 return; 228 } 229 230 lane = (state->interface == PHY_INTERFACE_MODE_10G_QXGMII) ? 231 MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; 232 233 st.mode = cpu_to_le32(FIELD_PREP(MXL862XX_XPCS_ST_PORT_ID, 234 mpcs->serdes_id) | 235 FIELD_PREP(MXL862XX_XPCS_ST_INTERFACE, if_mode) | 236 FIELD_PREP(MXL862XX_XPCS_ST_USX_SUBPORT, 237 mpcs->slot) | 238 FIELD_PREP(MXL862XX_XPCS_ST_USX_LANE_MODE, lane)); 239 240 ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st); 241 if (ret) { 242 state->link = false; 243 return; 244 } 245 246 mode = le32_to_cpu(st.mode); 247 state->link = FIELD_GET(MXL862XX_XPCS_ST_LINK, mode) && 248 !FIELD_GET(MXL862XX_XPCS_ST_PCS_FAULT, mode); 249 state->an_complete = FIELD_GET(MXL862XX_XPCS_ST_AN_COMPLETE, mode); 250 251 switch (state->interface) { 252 case PHY_INTERFACE_MODE_1000BASEX: 253 case PHY_INTERFACE_MODE_2500BASEX: 254 case PHY_INTERFACE_MODE_SGMII: 255 case PHY_INTERFACE_MODE_QSGMII: 256 bmsr = (state->link ? BMSR_LSTATUS : 0) | 257 (state->an_complete ? BMSR_ANEGCOMPLETE : 0); 258 phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, 259 le16_to_cpu(st.lpa.cl37)); 260 break; 261 262 case PHY_INTERFACE_MODE_USXGMII: 263 case PHY_INTERFACE_MODE_10G_QXGMII: 264 if (state->link) 265 phylink_decode_usxgmii_word(state, 266 le16_to_cpu(st.lpa.usx)); 267 break; 268 269 case PHY_INTERFACE_MODE_10GBASER: 270 case PHY_INTERFACE_MODE_10GKR: 271 if (state->link) { 272 state->speed = SPEED_10000; 273 state->duplex = DUPLEX_FULL; 274 } 275 break; 276 277 default: 278 state->link = false; 279 break; 280 } 281 } 282 283 static void mxl862xx_pcs_an_restart(struct phylink_pcs *pcs) 284 { 285 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 286 struct mxl862xx_priv *priv = mpcs->priv; 287 struct mxl862xx_xpcs_an_restart an = {}; 288 int if_mode, lane; 289 290 if_mode = mxl862xx_xpcs_if_mode(mpcs->interface); 291 if (if_mode < 0) 292 return; 293 294 lane = (mpcs->interface == PHY_INTERFACE_MODE_10G_QXGMII) ? 295 MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; 296 297 an.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_ANR_PORT_ID, 298 mpcs->serdes_id) | 299 FIELD_PREP(MXL862XX_XPCS_ANR_INTERFACE, if_mode) | 300 FIELD_PREP(MXL862XX_XPCS_ANR_USX_SUBPORT, 301 mpcs->slot) | 302 FIELD_PREP(MXL862XX_XPCS_ANR_USX_LANE_MODE, lane)); 303 304 MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an); 305 } 306 307 static void mxl862xx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, 308 phy_interface_t interface, int speed, 309 int duplex) 310 { 311 struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); 312 struct mxl862xx_xpcs_pcs_link_up lu = {}; 313 struct mxl862xx_priv *priv = mpcs->priv; 314 int if_mode, lane, dup; 315 316 /* With inband-AN enabled (role=MAC), the XPCS auto-resolves 317 * speed/duplex from the partner's AN word and the firmware 318 * short-circuits link_up. Skip the firmware round-trip, same 319 * as pcs-mtk-lynxi. 320 */ 321 if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) 322 return; 323 324 if_mode = mxl862xx_xpcs_if_mode(interface); 325 if (if_mode < 0) 326 return; 327 328 lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ? 329 MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; 330 dup = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL : 331 MXL862XX_XPCS_DUPLEX_HALF; 332 333 lu.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_LU_PORT_ID, 334 mpcs->serdes_id) | 335 FIELD_PREP(MXL862XX_XPCS_LU_INTERFACE, if_mode) | 336 FIELD_PREP(MXL862XX_XPCS_LU_USX_SUBPORT, 337 mpcs->slot) | 338 FIELD_PREP(MXL862XX_XPCS_LU_USX_LANE_MODE, lane) | 339 FIELD_PREP(MXL862XX_XPCS_LU_DUPLEX, dup)); 340 lu.speed = cpu_to_le16(speed); 341 342 MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_LINK_UP, lu); 343 } 344 345 static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs, 346 phy_interface_t interface) 347 { 348 switch (interface) { 349 case PHY_INTERFACE_MODE_SGMII: 350 case PHY_INTERFACE_MODE_QSGMII: 351 case PHY_INTERFACE_MODE_1000BASEX: 352 case PHY_INTERFACE_MODE_2500BASEX: 353 return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; 354 case PHY_INTERFACE_MODE_USXGMII: 355 case PHY_INTERFACE_MODE_10G_QXGMII: 356 case PHY_INTERFACE_MODE_10GKR: 357 return LINK_INBAND_ENABLE; 358 case PHY_INTERFACE_MODE_10GBASER: 359 return LINK_INBAND_DISABLE; 360 default: 361 return 0; 362 } 363 } 364 365 static const struct phylink_pcs_ops mxl862xx_pcs_ops = { 366 .pcs_enable = mxl862xx_pcs_enable, 367 .pcs_disable = mxl862xx_pcs_disable, 368 .pcs_config = mxl862xx_pcs_config, 369 .pcs_get_state = mxl862xx_pcs_get_state, 370 .pcs_an_restart = mxl862xx_pcs_an_restart, 371 .pcs_link_up = mxl862xx_pcs_link_up, 372 .pcs_inband_caps = mxl862xx_pcs_inband_caps, 373 }; 374 375 void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, 376 int port) 377 { 378 pcs->priv = priv; 379 pcs->serdes_id = MXL862XX_SERDES_PORT_ID(port); 380 pcs->slot = MXL862XX_SERDES_SLOT(port); 381 pcs->interface = PHY_INTERFACE_MODE_NA; 382 383 pcs->pcs.ops = &mxl862xx_pcs_ops; 384 pcs->pcs.poll = true; 385 386 __set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces); 387 __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, pcs->pcs.supported_interfaces); 388 if (pcs->slot != 0) 389 return; 390 391 __set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces); 392 __set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces); 393 __set_bit(PHY_INTERFACE_MODE_2500BASEX, pcs->pcs.supported_interfaces); 394 __set_bit(PHY_INTERFACE_MODE_10GBASER, pcs->pcs.supported_interfaces); 395 __set_bit(PHY_INTERFACE_MODE_10GKR, pcs->pcs.supported_interfaces); 396 __set_bit(PHY_INTERFACE_MODE_USXGMII, pcs->pcs.supported_interfaces); 397 } 398 399 static struct phylink_pcs * 400 mxl862xx_phylink_mac_select_pcs(struct phylink_config *config, 401 phy_interface_t interface) 402 { 403 struct dsa_port *dp = dsa_phylink_to_port(config); 404 struct mxl862xx_priv *priv = dp->ds->priv; 405 int port = dp->index; 406 407 switch (port) { 408 case 9 ... 16: 409 if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) { 410 dev_warn_once(dp->ds->dev, 411 "SerDes PCS unsupported on old firmware.\n"); 412 return NULL; 413 } 414 return &priv->serdes_ports[port - 9].pcs; 415 default: 416 return NULL; 417 } 418 } 419 420 static void mxl862xx_phylink_mac_config(struct phylink_config *config, 421 unsigned int mode, 422 const struct phylink_link_state *state) 423 { 424 } 425 426 static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, 427 unsigned int mode, 428 phy_interface_t interface) 429 { 430 } 431 432 static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, 433 struct phy_device *phydev, 434 unsigned int mode, 435 phy_interface_t interface, 436 int speed, int duplex, 437 bool tx_pause, bool rx_pause) 438 { 439 } 440 441 const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { 442 .mac_config = mxl862xx_phylink_mac_config, 443 .mac_link_down = mxl862xx_phylink_mac_link_down, 444 .mac_link_up = mxl862xx_phylink_mac_link_up, 445 .mac_select_pcs = mxl862xx_phylink_mac_select_pcs, 446 }; 447