linux/drivers/net/pcs/pcs-xpcs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
   4 * Synopsys DesignWare XPCS helpers
   5 *
   6 * Author: Jose Abreu <Jose.Abreu@synopsys.com>
   7 */
   8
   9#include <linux/delay.h>
  10#include <linux/pcs/pcs-xpcs.h>
  11#include <linux/mdio.h>
  12#include <linux/phylink.h>
  13#include <linux/workqueue.h>
  14#include "pcs-xpcs.h"
  15
  16#define phylink_pcs_to_xpcs(pl_pcs) \
  17        container_of((pl_pcs), struct dw_xpcs, pcs)
  18
  19static const int xpcs_usxgmii_features[] = {
  20        ETHTOOL_LINK_MODE_Pause_BIT,
  21        ETHTOOL_LINK_MODE_Asym_Pause_BIT,
  22        ETHTOOL_LINK_MODE_Autoneg_BIT,
  23        ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
  24        ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
  25        ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
  26        ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
  27        __ETHTOOL_LINK_MODE_MASK_NBITS,
  28};
  29
  30static const int xpcs_10gkr_features[] = {
  31        ETHTOOL_LINK_MODE_Pause_BIT,
  32        ETHTOOL_LINK_MODE_Asym_Pause_BIT,
  33        ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
  34        __ETHTOOL_LINK_MODE_MASK_NBITS,
  35};
  36
  37static const int xpcs_xlgmii_features[] = {
  38        ETHTOOL_LINK_MODE_Pause_BIT,
  39        ETHTOOL_LINK_MODE_Asym_Pause_BIT,
  40        ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
  41        ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
  42        ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
  43        ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
  44        ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
  45        ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
  46        ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
  47        ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
  48        ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
  49        ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
  50        ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
  51        ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
  52        ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
  53        ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
  54        ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
  55        ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
  56        ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
  57        ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
  58        ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
  59        ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
  60        ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
  61        ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
  62        ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
  63        ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
  64        __ETHTOOL_LINK_MODE_MASK_NBITS,
  65};
  66
  67static const int xpcs_sgmii_features[] = {
  68        ETHTOOL_LINK_MODE_Pause_BIT,
  69        ETHTOOL_LINK_MODE_Asym_Pause_BIT,
  70        ETHTOOL_LINK_MODE_Autoneg_BIT,
  71        ETHTOOL_LINK_MODE_10baseT_Half_BIT,
  72        ETHTOOL_LINK_MODE_10baseT_Full_BIT,
  73        ETHTOOL_LINK_MODE_100baseT_Half_BIT,
  74        ETHTOOL_LINK_MODE_100baseT_Full_BIT,
  75        ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
  76        ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
  77        __ETHTOOL_LINK_MODE_MASK_NBITS,
  78};
  79
  80static const int xpcs_2500basex_features[] = {
  81        ETHTOOL_LINK_MODE_Pause_BIT,
  82        ETHTOOL_LINK_MODE_Asym_Pause_BIT,
  83        ETHTOOL_LINK_MODE_Autoneg_BIT,
  84        ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
  85        ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
  86        __ETHTOOL_LINK_MODE_MASK_NBITS,
  87};
  88
  89static const phy_interface_t xpcs_usxgmii_interfaces[] = {
  90        PHY_INTERFACE_MODE_USXGMII,
  91};
  92
  93static const phy_interface_t xpcs_10gkr_interfaces[] = {
  94        PHY_INTERFACE_MODE_10GKR,
  95};
  96
  97static const phy_interface_t xpcs_xlgmii_interfaces[] = {
  98        PHY_INTERFACE_MODE_XLGMII,
  99};
 100
 101static const phy_interface_t xpcs_sgmii_interfaces[] = {
 102        PHY_INTERFACE_MODE_SGMII,
 103};
 104
 105static const phy_interface_t xpcs_2500basex_interfaces[] = {
 106        PHY_INTERFACE_MODE_2500BASEX,
 107        PHY_INTERFACE_MODE_MAX,
 108};
 109
 110enum {
 111        DW_XPCS_USXGMII,
 112        DW_XPCS_10GKR,
 113        DW_XPCS_XLGMII,
 114        DW_XPCS_SGMII,
 115        DW_XPCS_2500BASEX,
 116        DW_XPCS_INTERFACE_MAX,
 117};
 118
 119struct xpcs_compat {
 120        const int *supported;
 121        const phy_interface_t *interface;
 122        int num_interfaces;
 123        int an_mode;
 124        int (*pma_config)(struct dw_xpcs *xpcs);
 125};
 126
 127struct xpcs_id {
 128        u32 id;
 129        u32 mask;
 130        const struct xpcs_compat *compat;
 131};
 132
 133static const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id,
 134                                                  phy_interface_t interface)
 135{
 136        int i, j;
 137
 138        for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
 139                const struct xpcs_compat *compat = &id->compat[i];
 140
 141                for (j = 0; j < compat->num_interfaces; j++)
 142                        if (compat->interface[j] == interface)
 143                                return compat;
 144        }
 145
 146        return NULL;
 147}
 148
 149int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
 150{
 151        const struct xpcs_compat *compat;
 152
 153        compat = xpcs_find_compat(xpcs->id, interface);
 154        if (!compat)
 155                return -ENODEV;
 156
 157        return compat->an_mode;
 158}
 159EXPORT_SYMBOL_GPL(xpcs_get_an_mode);
 160
 161static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat,
 162                                      enum ethtool_link_mode_bit_indices linkmode)
 163{
 164        int i;
 165
 166        for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
 167                if (compat->supported[i] == linkmode)
 168                        return true;
 169
 170        return false;
 171}
 172
 173#define xpcs_linkmode_supported(compat, mode) \
 174        __xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT)
 175
 176int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg)
 177{
 178        struct mii_bus *bus = xpcs->mdiodev->bus;
 179        int addr = xpcs->mdiodev->addr;
 180
 181        return mdiobus_c45_read(bus, addr, dev, reg);
 182}
 183
 184int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val)
 185{
 186        struct mii_bus *bus = xpcs->mdiodev->bus;
 187        int addr = xpcs->mdiodev->addr;
 188
 189        return mdiobus_c45_write(bus, addr, dev, reg, val);
 190}
 191
 192static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
 193{
 194        return xpcs_read(xpcs, dev, DW_VENDOR | reg);
 195}
 196
 197static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
 198                             u16 val)
 199{
 200        return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
 201}
 202
 203static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
 204{
 205        return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
 206}
 207
 208static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
 209{
 210        return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
 211}
 212
 213static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev)
 214{
 215        /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
 216        unsigned int retries = 12;
 217        int ret;
 218
 219        do {
 220                msleep(50);
 221                ret = xpcs_read(xpcs, dev, MDIO_CTRL1);
 222                if (ret < 0)
 223                        return ret;
 224        } while (ret & MDIO_CTRL1_RESET && --retries);
 225
 226        return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0;
 227}
 228
 229static int xpcs_soft_reset(struct dw_xpcs *xpcs,
 230                           const struct xpcs_compat *compat)
 231{
 232        int ret, dev;
 233
 234        switch (compat->an_mode) {
 235        case DW_AN_C73:
 236                dev = MDIO_MMD_PCS;
 237                break;
 238        case DW_AN_C37_SGMII:
 239        case DW_2500BASEX:
 240                dev = MDIO_MMD_VEND2;
 241                break;
 242        default:
 243                return -1;
 244        }
 245
 246        ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET);
 247        if (ret < 0)
 248                return ret;
 249
 250        return xpcs_poll_reset(xpcs, dev);
 251}
 252
 253#define xpcs_warn(__xpcs, __state, __args...) \
 254({ \
 255        if ((__state)->link) \
 256                dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \
 257})
 258
 259static int xpcs_read_fault_c73(struct dw_xpcs *xpcs,
 260                               struct phylink_link_state *state)
 261{
 262        int ret;
 263
 264        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1);
 265        if (ret < 0)
 266                return ret;
 267
 268        if (ret & MDIO_STAT1_FAULT) {
 269                xpcs_warn(xpcs, state, "Link fault condition detected!\n");
 270                return -EFAULT;
 271        }
 272
 273        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2);
 274        if (ret < 0)
 275                return ret;
 276
 277        if (ret & MDIO_STAT2_RXFAULT)
 278                xpcs_warn(xpcs, state, "Receiver fault detected!\n");
 279        if (ret & MDIO_STAT2_TXFAULT)
 280                xpcs_warn(xpcs, state, "Transmitter fault detected!\n");
 281
 282        ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS);
 283        if (ret < 0)
 284                return ret;
 285
 286        if (ret & DW_RXFIFO_ERR) {
 287                xpcs_warn(xpcs, state, "FIFO fault condition detected!\n");
 288                return -EFAULT;
 289        }
 290
 291        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1);
 292        if (ret < 0)
 293                return ret;
 294
 295        if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK))
 296                xpcs_warn(xpcs, state, "Link is not locked!\n");
 297
 298        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2);
 299        if (ret < 0)
 300                return ret;
 301
 302        if (ret & MDIO_PCS_10GBRT_STAT2_ERR) {
 303                xpcs_warn(xpcs, state, "Link has errors!\n");
 304                return -EFAULT;
 305        }
 306
 307        return 0;
 308}
 309
 310static int xpcs_read_link_c73(struct dw_xpcs *xpcs, bool an)
 311{
 312        bool link = true;
 313        int ret;
 314
 315        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1);
 316        if (ret < 0)
 317                return ret;
 318
 319        if (!(ret & MDIO_STAT1_LSTATUS))
 320                link = false;
 321
 322        if (an) {
 323                ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1);
 324                if (ret < 0)
 325                        return ret;
 326
 327                if (!(ret & MDIO_STAT1_LSTATUS))
 328                        link = false;
 329        }
 330
 331        return link;
 332}
 333
 334static int xpcs_get_max_usxgmii_speed(const unsigned long *supported)
 335{
 336        int max = SPEED_UNKNOWN;
 337
 338        if (phylink_test(supported, 1000baseKX_Full))
 339                max = SPEED_1000;
 340        if (phylink_test(supported, 2500baseX_Full))
 341                max = SPEED_2500;
 342        if (phylink_test(supported, 10000baseKX4_Full))
 343                max = SPEED_10000;
 344        if (phylink_test(supported, 10000baseKR_Full))
 345                max = SPEED_10000;
 346
 347        return max;
 348}
 349
 350static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed)
 351{
 352        int ret, speed_sel;
 353
 354        switch (speed) {
 355        case SPEED_10:
 356                speed_sel = DW_USXGMII_10;
 357                break;
 358        case SPEED_100:
 359                speed_sel = DW_USXGMII_100;
 360                break;
 361        case SPEED_1000:
 362                speed_sel = DW_USXGMII_1000;
 363                break;
 364        case SPEED_2500:
 365                speed_sel = DW_USXGMII_2500;
 366                break;
 367        case SPEED_5000:
 368                speed_sel = DW_USXGMII_5000;
 369                break;
 370        case SPEED_10000:
 371                speed_sel = DW_USXGMII_10000;
 372                break;
 373        default:
 374                /* Nothing to do here */
 375                return;
 376        }
 377
 378        ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
 379        if (ret < 0)
 380                goto out;
 381
 382        ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN);
 383        if (ret < 0)
 384                goto out;
 385
 386        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1);
 387        if (ret < 0)
 388                goto out;
 389
 390        ret &= ~DW_USXGMII_SS_MASK;
 391        ret |= speed_sel | DW_USXGMII_FULL;
 392
 393        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
 394        if (ret < 0)
 395                goto out;
 396
 397        ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1);
 398        if (ret < 0)
 399                goto out;
 400
 401        ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST);
 402        if (ret < 0)
 403                goto out;
 404
 405        return;
 406
 407out:
 408        pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret));
 409}
 410
 411static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
 412                                 const struct xpcs_compat *compat)
 413{
 414        int ret, adv;
 415
 416        /* By default, in USXGMII mode XPCS operates at 10G baud and
 417         * replicates data to achieve lower speeds. Hereby, in this
 418         * default configuration we need to advertise all supported
 419         * modes and not only the ones we want to use.
 420         */
 421
 422        /* SR_AN_ADV3 */
 423        adv = 0;
 424        if (xpcs_linkmode_supported(compat, 2500baseX_Full))
 425                adv |= DW_C73_2500KX;
 426
 427        /* TODO: 5000baseKR */
 428
 429        ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv);
 430        if (ret < 0)
 431                return ret;
 432
 433        /* SR_AN_ADV2 */
 434        adv = 0;
 435        if (xpcs_linkmode_supported(compat, 1000baseKX_Full))
 436                adv |= DW_C73_1000KX;
 437        if (xpcs_linkmode_supported(compat, 10000baseKX4_Full))
 438                adv |= DW_C73_10000KX4;
 439        if (xpcs_linkmode_supported(compat, 10000baseKR_Full))
 440                adv |= DW_C73_10000KR;
 441
 442        ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv);
 443        if (ret < 0)
 444                return ret;
 445
 446        /* SR_AN_ADV1 */
 447        adv = DW_C73_AN_ADV_SF;
 448        if (xpcs_linkmode_supported(compat, Pause))
 449                adv |= DW_C73_PAUSE;
 450        if (xpcs_linkmode_supported(compat, Asym_Pause))
 451                adv |= DW_C73_ASYM_PAUSE;
 452
 453        return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv);
 454}
 455
 456static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs,
 457                                const struct xpcs_compat *compat)
 458{
 459        int ret;
 460
 461        ret = _xpcs_config_aneg_c73(xpcs, compat);
 462        if (ret < 0)
 463                return ret;
 464
 465        ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1);
 466        if (ret < 0)
 467                return ret;
 468
 469        ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
 470
 471        return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret);
 472}
 473
 474static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs,
 475                              struct phylink_link_state *state,
 476                              const struct xpcs_compat *compat)
 477{
 478        int ret;
 479
 480        ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1);
 481        if (ret < 0)
 482                return ret;
 483
 484        if (ret & MDIO_AN_STAT1_COMPLETE) {
 485                ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1);
 486                if (ret < 0)
 487                        return ret;
 488
 489                /* Check if Aneg outcome is valid */
 490                if (!(ret & DW_C73_AN_ADV_SF)) {
 491                        xpcs_config_aneg_c73(xpcs, compat);
 492                        return 0;
 493                }
 494
 495                return 1;
 496        }
 497
 498        return 0;
 499}
 500
 501static int xpcs_read_lpa_c73(struct dw_xpcs *xpcs,
 502                             struct phylink_link_state *state)
 503{
 504        int ret;
 505
 506        ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1);
 507        if (ret < 0)
 508                return ret;
 509
 510        if (!(ret & MDIO_AN_STAT1_LPABLE)) {
 511                phylink_clear(state->lp_advertising, Autoneg);
 512                return 0;
 513        }
 514
 515        phylink_set(state->lp_advertising, Autoneg);
 516
 517        /* Clause 73 outcome */
 518        ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL3);
 519        if (ret < 0)
 520                return ret;
 521
 522        if (ret & DW_C73_2500KX)
 523                phylink_set(state->lp_advertising, 2500baseX_Full);
 524
 525        ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL2);
 526        if (ret < 0)
 527                return ret;
 528
 529        if (ret & DW_C73_1000KX)
 530                phylink_set(state->lp_advertising, 1000baseKX_Full);
 531        if (ret & DW_C73_10000KX4)
 532                phylink_set(state->lp_advertising, 10000baseKX4_Full);
 533        if (ret & DW_C73_10000KR)
 534                phylink_set(state->lp_advertising, 10000baseKR_Full);
 535
 536        ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1);
 537        if (ret < 0)
 538                return ret;
 539
 540        if (ret & DW_C73_PAUSE)
 541                phylink_set(state->lp_advertising, Pause);
 542        if (ret & DW_C73_ASYM_PAUSE)
 543                phylink_set(state->lp_advertising, Asym_Pause);
 544
 545        linkmode_and(state->lp_advertising, state->lp_advertising,
 546                     state->advertising);
 547        return 0;
 548}
 549
 550static void xpcs_resolve_lpa_c73(struct dw_xpcs *xpcs,
 551                                 struct phylink_link_state *state)
 552{
 553        int max_speed = xpcs_get_max_usxgmii_speed(state->lp_advertising);
 554
 555        state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
 556        state->speed = max_speed;
 557        state->duplex = DUPLEX_FULL;
 558}
 559
 560static int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs,
 561                                     struct phylink_link_state *state)
 562{
 563        unsigned long *adv = state->advertising;
 564        int speed = SPEED_UNKNOWN;
 565        int bit;
 566
 567        for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) {
 568                int new_speed = SPEED_UNKNOWN;
 569
 570                switch (bit) {
 571                case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT:
 572                case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT:
 573                case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT:
 574                        new_speed = SPEED_25000;
 575                        break;
 576                case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT:
 577                case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT:
 578                case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT:
 579                case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT:
 580                        new_speed = SPEED_40000;
 581                        break;
 582                case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT:
 583                case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT:
 584                case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT:
 585                case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT:
 586                case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT:
 587                case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT:
 588                case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT:
 589                case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT:
 590                        new_speed = SPEED_50000;
 591                        break;
 592                case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT:
 593                case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT:
 594                case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT:
 595                case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT:
 596                case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT:
 597                case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT:
 598                case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT:
 599                case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT:
 600                case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT:
 601                        new_speed = SPEED_100000;
 602                        break;
 603                default:
 604                        continue;
 605                }
 606
 607                if (new_speed > speed)
 608                        speed = new_speed;
 609        }
 610
 611        return speed;
 612}
 613
 614static void xpcs_resolve_pma(struct dw_xpcs *xpcs,
 615                             struct phylink_link_state *state)
 616{
 617        state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
 618        state->duplex = DUPLEX_FULL;
 619
 620        switch (state->interface) {
 621        case PHY_INTERFACE_MODE_10GKR:
 622                state->speed = SPEED_10000;
 623                break;
 624        case PHY_INTERFACE_MODE_XLGMII:
 625                state->speed = xpcs_get_max_xlgmii_speed(xpcs, state);
 626                break;
 627        default:
 628                state->speed = SPEED_UNKNOWN;
 629                break;
 630        }
 631}
 632
 633static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
 634                         const struct phylink_link_state *state)
 635{
 636        __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, };
 637        const struct xpcs_compat *compat;
 638        struct dw_xpcs *xpcs;
 639        int i;
 640
 641        xpcs = phylink_pcs_to_xpcs(pcs);
 642        compat = xpcs_find_compat(xpcs->id, state->interface);
 643
 644        /* Populate the supported link modes for this PHY interface type.
 645         * FIXME: what about the port modes and autoneg bit? This masks
 646         * all those away.
 647         */
 648        if (compat)
 649                for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++)
 650                        set_bit(compat->supported[i], xpcs_supported);
 651
 652        linkmode_and(supported, supported, xpcs_supported);
 653
 654        return 0;
 655}
 656
 657void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces)
 658{
 659        int i, j;
 660
 661        for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) {
 662                const struct xpcs_compat *compat = &xpcs->id->compat[i];
 663
 664                for (j = 0; j < compat->num_interfaces; j++)
 665                        if (compat->interface[j] < PHY_INTERFACE_MODE_MAX)
 666                                __set_bit(compat->interface[j], interfaces);
 667        }
 668}
 669EXPORT_SYMBOL_GPL(xpcs_get_interfaces);
 670
 671int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
 672{
 673        int ret;
 674
 675        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0);
 676        if (ret < 0)
 677                return ret;
 678
 679        if (enable) {
 680        /* Enable EEE */
 681                ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
 682                      DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
 683                      DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
 684                      mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT;
 685        } else {
 686                ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
 687                       DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
 688                       DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
 689                       DW_VR_MII_EEE_MULT_FACT_100NS);
 690        }
 691
 692        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret);
 693        if (ret < 0)
 694                return ret;
 695
 696        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1);
 697        if (ret < 0)
 698                return ret;
 699
 700        if (enable)
 701                ret |= DW_VR_MII_EEE_TRN_LPI;
 702        else
 703                ret &= ~DW_VR_MII_EEE_TRN_LPI;
 704
 705        return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret);
 706}
 707EXPORT_SYMBOL_GPL(xpcs_config_eee);
 708
 709static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
 710{
 711        int ret, mdio_ctrl;
 712
 713        /* For AN for C37 SGMII mode, the settings are :-
 714         * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case
 715              it is already enabled)
 716         * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN)
 717         * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII)
 718         *    DW xPCS used with DW EQoS MAC is always MAC side SGMII.
 719         * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic
 720         *    speed/duplex mode change by HW after SGMII AN complete)
 721         * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN)
 722         *
 723         * Note: Since it is MAC side SGMII, there is no need to set
 724         *       SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from
 725         *       PHY about the link state change after C28 AN is completed
 726         *       between PHY and Link Partner. There is also no need to
 727         *       trigger AN restart for MAC-side SGMII.
 728         */
 729        mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
 730        if (mdio_ctrl < 0)
 731                return mdio_ctrl;
 732
 733        if (mdio_ctrl & AN_CL37_EN) {
 734                ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
 735                                 mdio_ctrl & ~AN_CL37_EN);
 736                if (ret < 0)
 737                        return ret;
 738        }
 739
 740        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
 741        if (ret < 0)
 742                return ret;
 743
 744        ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK);
 745        ret |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
 746                DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
 747                DW_VR_MII_PCS_MODE_MASK);
 748        ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII <<
 749                DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
 750                DW_VR_MII_TX_CONFIG_MASK);
 751        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret);
 752        if (ret < 0)
 753                return ret;
 754
 755        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
 756        if (ret < 0)
 757                return ret;
 758
 759        if (phylink_autoneg_inband(mode))
 760                ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 761        else
 762                ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 763
 764        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
 765        if (ret < 0)
 766                return ret;
 767
 768        if (phylink_autoneg_inband(mode))
 769                ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
 770                                 mdio_ctrl | AN_CL37_EN);
 771
 772        return ret;
 773}
 774
 775static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
 776{
 777        int ret;
 778
 779        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
 780        if (ret < 0)
 781                return ret;
 782        ret |= DW_VR_MII_DIG_CTRL1_2G5_EN;
 783        ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 784        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
 785        if (ret < 0)
 786                return ret;
 787
 788        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL);
 789        if (ret < 0)
 790                return ret;
 791        ret &= ~AN_CL37_EN;
 792        ret |= SGMII_SPEED_SS6;
 793        ret &= ~SGMII_SPEED_SS13;
 794        return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret);
 795}
 796
 797int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
 798                   unsigned int mode)
 799{
 800        const struct xpcs_compat *compat;
 801        int ret;
 802
 803        compat = xpcs_find_compat(xpcs->id, interface);
 804        if (!compat)
 805                return -ENODEV;
 806
 807        switch (compat->an_mode) {
 808        case DW_AN_C73:
 809                if (phylink_autoneg_inband(mode)) {
 810                        ret = xpcs_config_aneg_c73(xpcs, compat);
 811                        if (ret)
 812                                return ret;
 813                }
 814                break;
 815        case DW_AN_C37_SGMII:
 816                ret = xpcs_config_aneg_c37_sgmii(xpcs, mode);
 817                if (ret)
 818                        return ret;
 819                break;
 820        case DW_2500BASEX:
 821                ret = xpcs_config_2500basex(xpcs);
 822                if (ret)
 823                        return ret;
 824                break;
 825        default:
 826                return -1;
 827        }
 828
 829        if (compat->pma_config) {
 830                ret = compat->pma_config(xpcs);
 831                if (ret)
 832                        return ret;
 833        }
 834
 835        return 0;
 836}
 837EXPORT_SYMBOL_GPL(xpcs_do_config);
 838
 839static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode,
 840                       phy_interface_t interface,
 841                       const unsigned long *advertising,
 842                       bool permit_pause_to_mac)
 843{
 844        struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
 845
 846        return xpcs_do_config(xpcs, interface, mode);
 847}
 848
 849static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
 850                              struct phylink_link_state *state,
 851                              const struct xpcs_compat *compat)
 852{
 853        int ret;
 854
 855        /* Link needs to be read first ... */
 856        state->link = xpcs_read_link_c73(xpcs, state->an_enabled) > 0 ? 1 : 0;
 857
 858        /* ... and then we check the faults. */
 859        ret = xpcs_read_fault_c73(xpcs, state);
 860        if (ret) {
 861                ret = xpcs_soft_reset(xpcs, compat);
 862                if (ret)
 863                        return ret;
 864
 865                state->link = 0;
 866
 867                return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND);
 868        }
 869
 870        if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) {
 871                state->an_complete = true;
 872                xpcs_read_lpa_c73(xpcs, state);
 873                xpcs_resolve_lpa_c73(xpcs, state);
 874        } else if (state->an_enabled) {
 875                state->link = 0;
 876        } else if (state->link) {
 877                xpcs_resolve_pma(xpcs, state);
 878        }
 879
 880        return 0;
 881}
 882
 883static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
 884                                    struct phylink_link_state *state)
 885{
 886        int ret;
 887
 888        /* Reset link_state */
 889        state->link = false;
 890        state->speed = SPEED_UNKNOWN;
 891        state->duplex = DUPLEX_UNKNOWN;
 892        state->pause = 0;
 893
 894        /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link
 895         * status, speed and duplex.
 896         */
 897        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS);
 898        if (ret < 0)
 899                return ret;
 900
 901        if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) {
 902                int speed_value;
 903
 904                state->link = true;
 905
 906                speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >>
 907                              DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT;
 908                if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000)
 909                        state->speed = SPEED_1000;
 910                else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100)
 911                        state->speed = SPEED_100;
 912                else
 913                        state->speed = SPEED_10;
 914
 915                if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD)
 916                        state->duplex = DUPLEX_FULL;
 917                else
 918                        state->duplex = DUPLEX_HALF;
 919        }
 920
 921        return 0;
 922}
 923
 924static void xpcs_get_state(struct phylink_pcs *pcs,
 925                           struct phylink_link_state *state)
 926{
 927        struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
 928        const struct xpcs_compat *compat;
 929        int ret;
 930
 931        compat = xpcs_find_compat(xpcs->id, state->interface);
 932        if (!compat)
 933                return;
 934
 935        switch (compat->an_mode) {
 936        case DW_AN_C73:
 937                ret = xpcs_get_state_c73(xpcs, state, compat);
 938                if (ret) {
 939                        pr_err("xpcs_get_state_c73 returned %pe\n",
 940                               ERR_PTR(ret));
 941                        return;
 942                }
 943                break;
 944        case DW_AN_C37_SGMII:
 945                ret = xpcs_get_state_c37_sgmii(xpcs, state);
 946                if (ret) {
 947                        pr_err("xpcs_get_state_c37_sgmii returned %pe\n",
 948                               ERR_PTR(ret));
 949                }
 950                break;
 951        default:
 952                return;
 953        }
 954}
 955
 956static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
 957                               int speed, int duplex)
 958{
 959        int val, ret;
 960
 961        if (phylink_autoneg_inband(mode))
 962                return;
 963
 964        switch (speed) {
 965        case SPEED_1000:
 966                val = BMCR_SPEED1000;
 967                break;
 968        case SPEED_100:
 969                val = BMCR_SPEED100;
 970                break;
 971        case SPEED_10:
 972                val = BMCR_SPEED10;
 973                break;
 974        default:
 975                return;
 976        }
 977
 978        if (duplex == DUPLEX_FULL)
 979                val |= BMCR_FULLDPLX;
 980
 981        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
 982        if (ret)
 983                pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
 984}
 985
 986void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
 987                  phy_interface_t interface, int speed, int duplex)
 988{
 989        struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
 990
 991        if (interface == PHY_INTERFACE_MODE_USXGMII)
 992                return xpcs_config_usxgmii(xpcs, speed);
 993        if (interface == PHY_INTERFACE_MODE_SGMII)
 994                return xpcs_link_up_sgmii(xpcs, mode, speed, duplex);
 995}
 996EXPORT_SYMBOL_GPL(xpcs_link_up);
 997
 998static u32 xpcs_get_id(struct dw_xpcs *xpcs)
 999{
1000        int ret;
1001        u32 id;
1002
1003        /* First, search C73 PCS using PCS MMD */
1004        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1);
1005        if (ret < 0)
1006                return 0xffffffff;
1007
1008        id = ret << 16;
1009
1010        ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2);
1011        if (ret < 0)
1012                return 0xffffffff;
1013
1014        /* If Device IDs are not all zeros or all ones,
1015         * we found C73 AN-type device
1016         */
1017        if ((id | ret) && (id | ret) != 0xffffffff)
1018                return id | ret;
1019
1020        /* Next, search C37 PCS using Vendor-Specific MII MMD */
1021        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1);
1022        if (ret < 0)
1023                return 0xffffffff;
1024
1025        id = ret << 16;
1026
1027        ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2);
1028        if (ret < 0)
1029                return 0xffffffff;
1030
1031        /* If Device IDs are not all zeros, we found C37 AN-type device */
1032        if (id | ret)
1033                return id | ret;
1034
1035        return 0xffffffff;
1036}
1037
1038static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1039        [DW_XPCS_USXGMII] = {
1040                .supported = xpcs_usxgmii_features,
1041                .interface = xpcs_usxgmii_interfaces,
1042                .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces),
1043                .an_mode = DW_AN_C73,
1044        },
1045        [DW_XPCS_10GKR] = {
1046                .supported = xpcs_10gkr_features,
1047                .interface = xpcs_10gkr_interfaces,
1048                .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces),
1049                .an_mode = DW_AN_C73,
1050        },
1051        [DW_XPCS_XLGMII] = {
1052                .supported = xpcs_xlgmii_features,
1053                .interface = xpcs_xlgmii_interfaces,
1054                .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces),
1055                .an_mode = DW_AN_C73,
1056        },
1057        [DW_XPCS_SGMII] = {
1058                .supported = xpcs_sgmii_features,
1059                .interface = xpcs_sgmii_interfaces,
1060                .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1061                .an_mode = DW_AN_C37_SGMII,
1062        },
1063        [DW_XPCS_2500BASEX] = {
1064                .supported = xpcs_2500basex_features,
1065                .interface = xpcs_2500basex_interfaces,
1066                .num_interfaces = ARRAY_SIZE(xpcs_2500basex_features),
1067                .an_mode = DW_2500BASEX,
1068        },
1069};
1070
1071static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1072        [DW_XPCS_SGMII] = {
1073                .supported = xpcs_sgmii_features,
1074                .interface = xpcs_sgmii_interfaces,
1075                .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1076                .an_mode = DW_AN_C37_SGMII,
1077                .pma_config = nxp_sja1105_sgmii_pma_config,
1078        },
1079};
1080
1081static const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = {
1082        [DW_XPCS_SGMII] = {
1083                .supported = xpcs_sgmii_features,
1084                .interface = xpcs_sgmii_interfaces,
1085                .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces),
1086                .an_mode = DW_AN_C37_SGMII,
1087                .pma_config = nxp_sja1110_sgmii_pma_config,
1088        },
1089        [DW_XPCS_2500BASEX] = {
1090                .supported = xpcs_2500basex_features,
1091                .interface = xpcs_2500basex_interfaces,
1092                .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
1093                .an_mode = DW_2500BASEX,
1094                .pma_config = nxp_sja1110_2500basex_pma_config,
1095        },
1096};
1097
1098static const struct xpcs_id xpcs_id_list[] = {
1099        {
1100                .id = SYNOPSYS_XPCS_ID,
1101                .mask = SYNOPSYS_XPCS_MASK,
1102                .compat = synopsys_xpcs_compat,
1103        }, {
1104                .id = NXP_SJA1105_XPCS_ID,
1105                .mask = SYNOPSYS_XPCS_MASK,
1106                .compat = nxp_sja1105_xpcs_compat,
1107        }, {
1108                .id = NXP_SJA1110_XPCS_ID,
1109                .mask = SYNOPSYS_XPCS_MASK,
1110                .compat = nxp_sja1110_xpcs_compat,
1111        },
1112};
1113
1114static const struct phylink_pcs_ops xpcs_phylink_ops = {
1115        .pcs_validate = xpcs_validate,
1116        .pcs_config = xpcs_config,
1117        .pcs_get_state = xpcs_get_state,
1118        .pcs_link_up = xpcs_link_up,
1119};
1120
1121struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
1122                            phy_interface_t interface)
1123{
1124        struct dw_xpcs *xpcs;
1125        u32 xpcs_id;
1126        int i, ret;
1127
1128        xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL);
1129        if (!xpcs)
1130                return ERR_PTR(-ENOMEM);
1131
1132        xpcs->mdiodev = mdiodev;
1133
1134        xpcs_id = xpcs_get_id(xpcs);
1135
1136        for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) {
1137                const struct xpcs_id *entry = &xpcs_id_list[i];
1138                const struct xpcs_compat *compat;
1139
1140                if ((xpcs_id & entry->mask) != entry->id)
1141                        continue;
1142
1143                xpcs->id = entry;
1144
1145                compat = xpcs_find_compat(entry, interface);
1146                if (!compat) {
1147                        ret = -ENODEV;
1148                        goto out;
1149                }
1150
1151                xpcs->pcs.ops = &xpcs_phylink_ops;
1152                xpcs->pcs.poll = true;
1153
1154                ret = xpcs_soft_reset(xpcs, compat);
1155                if (ret)
1156                        goto out;
1157
1158                return xpcs;
1159        }
1160
1161        ret = -ENODEV;
1162
1163out:
1164        kfree(xpcs);
1165
1166        return ERR_PTR(ret);
1167}
1168EXPORT_SYMBOL_GPL(xpcs_create);
1169
1170void xpcs_destroy(struct dw_xpcs *xpcs)
1171{
1172        kfree(xpcs);
1173}
1174EXPORT_SYMBOL_GPL(xpcs_destroy);
1175
1176MODULE_LICENSE("GPL v2");
1177