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