linux/drivers/net/phy/mxl-gpy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Copyright (C) 2021 Maxlinear Corporation
   3 * Copyright (C) 2020 Intel Corporation
   4 *
   5 * Drivers for Maxlinear Ethernet GPY
   6 *
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/bitfield.h>
  11#include <linux/phy.h>
  12#include <linux/netdevice.h>
  13
  14/* PHY ID */
  15#define PHY_ID_GPYx15B_MASK     0xFFFFFFFC
  16#define PHY_ID_GPY21xB_MASK     0xFFFFFFF9
  17#define PHY_ID_GPY2xx           0x67C9DC00
  18#define PHY_ID_GPY115B          0x67C9DF00
  19#define PHY_ID_GPY115C          0x67C9DF10
  20#define PHY_ID_GPY211B          0x67C9DE08
  21#define PHY_ID_GPY211C          0x67C9DE10
  22#define PHY_ID_GPY212B          0x67C9DE09
  23#define PHY_ID_GPY212C          0x67C9DE20
  24#define PHY_ID_GPY215B          0x67C9DF04
  25#define PHY_ID_GPY215C          0x67C9DF20
  26#define PHY_ID_GPY241B          0x67C9DE40
  27#define PHY_ID_GPY241BM         0x67C9DE80
  28#define PHY_ID_GPY245B          0x67C9DEC0
  29
  30#define PHY_MIISTAT             0x18    /* MII state */
  31#define PHY_IMASK               0x19    /* interrupt mask */
  32#define PHY_ISTAT               0x1A    /* interrupt status */
  33#define PHY_FWV                 0x1E    /* firmware version */
  34
  35#define PHY_MIISTAT_SPD_MASK    GENMASK(2, 0)
  36#define PHY_MIISTAT_DPX         BIT(3)
  37#define PHY_MIISTAT_LS          BIT(10)
  38
  39#define PHY_MIISTAT_SPD_10      0
  40#define PHY_MIISTAT_SPD_100     1
  41#define PHY_MIISTAT_SPD_1000    2
  42#define PHY_MIISTAT_SPD_2500    4
  43
  44#define PHY_IMASK_WOL           BIT(15) /* Wake-on-LAN */
  45#define PHY_IMASK_ANC           BIT(10) /* Auto-Neg complete */
  46#define PHY_IMASK_ADSC          BIT(5)  /* Link auto-downspeed detect */
  47#define PHY_IMASK_DXMC          BIT(2)  /* Duplex mode change */
  48#define PHY_IMASK_LSPC          BIT(1)  /* Link speed change */
  49#define PHY_IMASK_LSTC          BIT(0)  /* Link state change */
  50#define PHY_IMASK_MASK          (PHY_IMASK_LSTC | \
  51                                 PHY_IMASK_LSPC | \
  52                                 PHY_IMASK_DXMC | \
  53                                 PHY_IMASK_ADSC | \
  54                                 PHY_IMASK_ANC)
  55
  56#define PHY_FWV_REL_MASK        BIT(15)
  57#define PHY_FWV_TYPE_MASK       GENMASK(11, 8)
  58#define PHY_FWV_MINOR_MASK      GENMASK(7, 0)
  59
  60/* SGMII */
  61#define VSPEC1_SGMII_CTRL       0x08
  62#define VSPEC1_SGMII_CTRL_ANEN  BIT(12)         /* Aneg enable */
  63#define VSPEC1_SGMII_CTRL_ANRS  BIT(9)          /* Restart Aneg */
  64#define VSPEC1_SGMII_ANEN_ANRS  (VSPEC1_SGMII_CTRL_ANEN | \
  65                                 VSPEC1_SGMII_CTRL_ANRS)
  66
  67/* WoL */
  68#define VPSPEC2_WOL_CTL         0x0E06
  69#define VPSPEC2_WOL_AD01        0x0E08
  70#define VPSPEC2_WOL_AD23        0x0E09
  71#define VPSPEC2_WOL_AD45        0x0E0A
  72#define WOL_EN                  BIT(0)
  73
  74static const struct {
  75        int type;
  76        int minor;
  77} ver_need_sgmii_reaneg[] = {
  78        {7, 0x6D},
  79        {8, 0x6D},
  80        {9, 0x73},
  81};
  82
  83static int gpy_config_init(struct phy_device *phydev)
  84{
  85        int ret;
  86
  87        /* Mask all interrupts */
  88        ret = phy_write(phydev, PHY_IMASK, 0);
  89        if (ret)
  90                return ret;
  91
  92        /* Clear all pending interrupts */
  93        ret = phy_read(phydev, PHY_ISTAT);
  94        return ret < 0 ? ret : 0;
  95}
  96
  97static int gpy_probe(struct phy_device *phydev)
  98{
  99        int ret;
 100
 101        if (!phydev->is_c45) {
 102                ret = phy_get_c45_ids(phydev);
 103                if (ret < 0)
 104                        return ret;
 105        }
 106
 107        /* Show GPY PHY FW version in dmesg */
 108        ret = phy_read(phydev, PHY_FWV);
 109        if (ret < 0)
 110                return ret;
 111
 112        phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
 113                    (ret & PHY_FWV_REL_MASK) ? "release" : "test");
 114
 115        return 0;
 116}
 117
 118static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
 119{
 120        int fw_ver, fw_type, fw_minor;
 121        size_t i;
 122
 123        fw_ver = phy_read(phydev, PHY_FWV);
 124        if (fw_ver < 0)
 125                return true;
 126
 127        fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
 128        fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
 129
 130        for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
 131                if (fw_type != ver_need_sgmii_reaneg[i].type)
 132                        continue;
 133                if (fw_minor < ver_need_sgmii_reaneg[i].minor)
 134                        return true;
 135                break;
 136        }
 137
 138        return false;
 139}
 140
 141static bool gpy_2500basex_chk(struct phy_device *phydev)
 142{
 143        int ret;
 144
 145        ret = phy_read(phydev, PHY_MIISTAT);
 146        if (ret < 0) {
 147                phydev_err(phydev, "Error: MDIO register access failed: %d\n",
 148                           ret);
 149                return false;
 150        }
 151
 152        if (!(ret & PHY_MIISTAT_LS) ||
 153            FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
 154                return false;
 155
 156        phydev->speed = SPEED_2500;
 157        phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
 158        phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
 159                       VSPEC1_SGMII_CTRL_ANEN, 0);
 160        return true;
 161}
 162
 163static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
 164{
 165        int ret;
 166
 167        ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
 168        if (ret < 0) {
 169                phydev_err(phydev, "Error: MMD register access failed: %d\n",
 170                           ret);
 171                return true;
 172        }
 173
 174        return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
 175}
 176
 177static int gpy_config_aneg(struct phy_device *phydev)
 178{
 179        bool changed = false;
 180        u32 adv;
 181        int ret;
 182
 183        if (phydev->autoneg == AUTONEG_DISABLE) {
 184                /* Configure half duplex with genphy_setup_forced,
 185                 * because genphy_c45_pma_setup_forced does not support.
 186                 */
 187                return phydev->duplex != DUPLEX_FULL
 188                        ? genphy_setup_forced(phydev)
 189                        : genphy_c45_pma_setup_forced(phydev);
 190        }
 191
 192        ret = genphy_c45_an_config_aneg(phydev);
 193        if (ret < 0)
 194                return ret;
 195        if (ret > 0)
 196                changed = true;
 197
 198        adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
 199        ret = phy_modify_changed(phydev, MII_CTRL1000,
 200                                 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
 201                                 adv);
 202        if (ret < 0)
 203                return ret;
 204        if (ret > 0)
 205                changed = true;
 206
 207        ret = genphy_c45_check_and_restart_aneg(phydev, changed);
 208        if (ret < 0)
 209                return ret;
 210
 211        if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
 212            phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
 213                return 0;
 214
 215        /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
 216         * disabled.
 217         */
 218        if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
 219            !gpy_sgmii_aneg_en(phydev))
 220                return 0;
 221
 222        /* There is a design constraint in GPY2xx device where SGMII AN is
 223         * only triggered when there is change of speed. If, PHY link
 224         * partner`s speed is still same even after PHY TPI is down and up
 225         * again, SGMII AN is not triggered and hence no new in-band message
 226         * from GPY to MAC side SGMII.
 227         * This could cause an issue during power up, when PHY is up prior to
 228         * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
 229         * wouldn`t receive new in-band message from GPY with correct link
 230         * status, speed and duplex info.
 231         *
 232         * 1) If PHY is already up and TPI link status is still down (such as
 233         *    hard reboot), TPI link status is polled for 4 seconds before
 234         *    retriggerring SGMII AN.
 235         * 2) If PHY is already up and TPI link status is also up (such as soft
 236         *    reboot), polling of TPI link status is not needed and SGMII AN is
 237         *    immediately retriggered.
 238         * 3) Other conditions such as PHY is down, speed change etc, skip
 239         *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
 240         *    initiate SGMII AN.
 241         */
 242
 243        if (phydev->state != PHY_UP)
 244                return 0;
 245
 246        ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
 247                                    20000, 4000000, false);
 248        if (ret == -ETIMEDOUT)
 249                return 0;
 250        else if (ret < 0)
 251                return ret;
 252
 253        /* Trigger SGMII AN. */
 254        return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
 255                              VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
 256}
 257
 258static void gpy_update_interface(struct phy_device *phydev)
 259{
 260        int ret;
 261
 262        /* Interface mode is fixed for USXGMII and integrated PHY */
 263        if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
 264            phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
 265                return;
 266
 267        /* Automatically switch SERDES interface between SGMII and 2500-BaseX
 268         * according to speed. Disable ANEG in 2500-BaseX mode.
 269         */
 270        switch (phydev->speed) {
 271        case SPEED_2500:
 272                phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
 273                ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
 274                                     VSPEC1_SGMII_CTRL_ANEN, 0);
 275                if (ret < 0)
 276                        phydev_err(phydev,
 277                                   "Error: Disable of SGMII ANEG failed: %d\n",
 278                                   ret);
 279                break;
 280        case SPEED_1000:
 281        case SPEED_100:
 282        case SPEED_10:
 283                phydev->interface = PHY_INTERFACE_MODE_SGMII;
 284                if (gpy_sgmii_aneg_en(phydev))
 285                        break;
 286                /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
 287                 * if ANEG is disabled (in 2500-BaseX mode).
 288                 */
 289                ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
 290                                     VSPEC1_SGMII_ANEN_ANRS,
 291                                     VSPEC1_SGMII_ANEN_ANRS);
 292                if (ret < 0)
 293                        phydev_err(phydev,
 294                                   "Error: Enable of SGMII ANEG failed: %d\n",
 295                                   ret);
 296                break;
 297        }
 298}
 299
 300static int gpy_read_status(struct phy_device *phydev)
 301{
 302        int ret;
 303
 304        ret = genphy_update_link(phydev);
 305        if (ret)
 306                return ret;
 307
 308        phydev->speed = SPEED_UNKNOWN;
 309        phydev->duplex = DUPLEX_UNKNOWN;
 310        phydev->pause = 0;
 311        phydev->asym_pause = 0;
 312
 313        if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
 314                ret = genphy_c45_read_lpa(phydev);
 315                if (ret < 0)
 316                        return ret;
 317
 318                /* Read the link partner's 1G advertisement */
 319                ret = phy_read(phydev, MII_STAT1000);
 320                if (ret < 0)
 321                        return ret;
 322                mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
 323        } else if (phydev->autoneg == AUTONEG_DISABLE) {
 324                linkmode_zero(phydev->lp_advertising);
 325        }
 326
 327        ret = phy_read(phydev, PHY_MIISTAT);
 328        if (ret < 0)
 329                return ret;
 330
 331        phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
 332        phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
 333        switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
 334        case PHY_MIISTAT_SPD_10:
 335                phydev->speed = SPEED_10;
 336                break;
 337        case PHY_MIISTAT_SPD_100:
 338                phydev->speed = SPEED_100;
 339                break;
 340        case PHY_MIISTAT_SPD_1000:
 341                phydev->speed = SPEED_1000;
 342                break;
 343        case PHY_MIISTAT_SPD_2500:
 344                phydev->speed = SPEED_2500;
 345                break;
 346        }
 347
 348        if (phydev->link)
 349                gpy_update_interface(phydev);
 350
 351        return 0;
 352}
 353
 354static int gpy_config_intr(struct phy_device *phydev)
 355{
 356        u16 mask = 0;
 357
 358        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 359                mask = PHY_IMASK_MASK;
 360
 361        return phy_write(phydev, PHY_IMASK, mask);
 362}
 363
 364static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
 365{
 366        int reg;
 367
 368        reg = phy_read(phydev, PHY_ISTAT);
 369        if (reg < 0) {
 370                phy_error(phydev);
 371                return IRQ_NONE;
 372        }
 373
 374        if (!(reg & PHY_IMASK_MASK))
 375                return IRQ_NONE;
 376
 377        phy_trigger_machine(phydev);
 378
 379        return IRQ_HANDLED;
 380}
 381
 382static int gpy_set_wol(struct phy_device *phydev,
 383                       struct ethtool_wolinfo *wol)
 384{
 385        struct net_device *attach_dev = phydev->attached_dev;
 386        int ret;
 387
 388        if (wol->wolopts & WAKE_MAGIC) {
 389                /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
 390                 * VPSPEC2_WOL_AD45 = Byte0:Byte1
 391                 * VPSPEC2_WOL_AD23 = Byte2:Byte3
 392                 * VPSPEC2_WOL_AD01 = Byte4:Byte5
 393                 */
 394                ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
 395                                       VPSPEC2_WOL_AD45,
 396                                       ((attach_dev->dev_addr[0] << 8) |
 397                                       attach_dev->dev_addr[1]));
 398                if (ret < 0)
 399                        return ret;
 400
 401                ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
 402                                       VPSPEC2_WOL_AD23,
 403                                       ((attach_dev->dev_addr[2] << 8) |
 404                                       attach_dev->dev_addr[3]));
 405                if (ret < 0)
 406                        return ret;
 407
 408                ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
 409                                       VPSPEC2_WOL_AD01,
 410                                       ((attach_dev->dev_addr[4] << 8) |
 411                                       attach_dev->dev_addr[5]));
 412                if (ret < 0)
 413                        return ret;
 414
 415                /* Enable the WOL interrupt */
 416                ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
 417                if (ret < 0)
 418                        return ret;
 419
 420                /* Enable magic packet matching */
 421                ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
 422                                       VPSPEC2_WOL_CTL,
 423                                       WOL_EN);
 424                if (ret < 0)
 425                        return ret;
 426
 427                /* Clear the interrupt status register.
 428                 * Only WoL is enabled so clear all.
 429                 */
 430                ret = phy_read(phydev, PHY_ISTAT);
 431                if (ret < 0)
 432                        return ret;
 433        } else {
 434                /* Disable magic packet matching */
 435                ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
 436                                         VPSPEC2_WOL_CTL,
 437                                         WOL_EN);
 438                if (ret < 0)
 439                        return ret;
 440        }
 441
 442        if (wol->wolopts & WAKE_PHY) {
 443                /* Enable the link state change interrupt */
 444                ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
 445                if (ret < 0)
 446                        return ret;
 447
 448                /* Clear the interrupt status register */
 449                ret = phy_read(phydev, PHY_ISTAT);
 450                if (ret < 0)
 451                        return ret;
 452
 453                if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
 454                        phy_trigger_machine(phydev);
 455
 456                return 0;
 457        }
 458
 459        /* Disable the link state change interrupt */
 460        return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
 461}
 462
 463static void gpy_get_wol(struct phy_device *phydev,
 464                        struct ethtool_wolinfo *wol)
 465{
 466        int ret;
 467
 468        wol->supported = WAKE_MAGIC | WAKE_PHY;
 469        wol->wolopts = 0;
 470
 471        ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
 472        if (ret & WOL_EN)
 473                wol->wolopts |= WAKE_MAGIC;
 474
 475        ret = phy_read(phydev, PHY_IMASK);
 476        if (ret & PHY_IMASK_LSTC)
 477                wol->wolopts |= WAKE_PHY;
 478}
 479
 480static int gpy_loopback(struct phy_device *phydev, bool enable)
 481{
 482        int ret;
 483
 484        ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
 485                         enable ? BMCR_LOOPBACK : 0);
 486        if (!ret) {
 487                /* It takes some time for PHY device to switch
 488                 * into/out-of loopback mode.
 489                 */
 490                msleep(100);
 491        }
 492
 493        return ret;
 494}
 495
 496static int gpy115_loopback(struct phy_device *phydev, bool enable)
 497{
 498        int ret;
 499        int fw_minor;
 500
 501        if (enable)
 502                return gpy_loopback(phydev, enable);
 503
 504        ret = phy_read(phydev, PHY_FWV);
 505        if (ret < 0)
 506                return ret;
 507
 508        fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, ret);
 509        if (fw_minor > 0x0076)
 510                return gpy_loopback(phydev, 0);
 511
 512        return genphy_soft_reset(phydev);
 513}
 514
 515static struct phy_driver gpy_drivers[] = {
 516        {
 517                PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
 518                .name           = "Maxlinear Ethernet GPY2xx",
 519                .get_features   = genphy_c45_pma_read_abilities,
 520                .config_init    = gpy_config_init,
 521                .probe          = gpy_probe,
 522                .suspend        = genphy_suspend,
 523                .resume         = genphy_resume,
 524                .config_aneg    = gpy_config_aneg,
 525                .aneg_done      = genphy_c45_aneg_done,
 526                .read_status    = gpy_read_status,
 527                .config_intr    = gpy_config_intr,
 528                .handle_interrupt = gpy_handle_interrupt,
 529                .set_wol        = gpy_set_wol,
 530                .get_wol        = gpy_get_wol,
 531                .set_loopback   = gpy_loopback,
 532        },
 533        {
 534                .phy_id         = PHY_ID_GPY115B,
 535                .phy_id_mask    = PHY_ID_GPYx15B_MASK,
 536                .name           = "Maxlinear Ethernet GPY115B",
 537                .get_features   = genphy_c45_pma_read_abilities,
 538                .config_init    = gpy_config_init,
 539                .probe          = gpy_probe,
 540                .suspend        = genphy_suspend,
 541                .resume         = genphy_resume,
 542                .config_aneg    = gpy_config_aneg,
 543                .aneg_done      = genphy_c45_aneg_done,
 544                .read_status    = gpy_read_status,
 545                .config_intr    = gpy_config_intr,
 546                .handle_interrupt = gpy_handle_interrupt,
 547                .set_wol        = gpy_set_wol,
 548                .get_wol        = gpy_get_wol,
 549                .set_loopback   = gpy115_loopback,
 550        },
 551        {
 552                PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
 553                .name           = "Maxlinear Ethernet GPY115C",
 554                .get_features   = genphy_c45_pma_read_abilities,
 555                .config_init    = gpy_config_init,
 556                .probe          = gpy_probe,
 557                .suspend        = genphy_suspend,
 558                .resume         = genphy_resume,
 559                .config_aneg    = gpy_config_aneg,
 560                .aneg_done      = genphy_c45_aneg_done,
 561                .read_status    = gpy_read_status,
 562                .config_intr    = gpy_config_intr,
 563                .handle_interrupt = gpy_handle_interrupt,
 564                .set_wol        = gpy_set_wol,
 565                .get_wol        = gpy_get_wol,
 566                .set_loopback   = gpy115_loopback,
 567        },
 568        {
 569                .phy_id         = PHY_ID_GPY211B,
 570                .phy_id_mask    = PHY_ID_GPY21xB_MASK,
 571                .name           = "Maxlinear Ethernet GPY211B",
 572                .get_features   = genphy_c45_pma_read_abilities,
 573                .config_init    = gpy_config_init,
 574                .probe          = gpy_probe,
 575                .suspend        = genphy_suspend,
 576                .resume         = genphy_resume,
 577                .config_aneg    = gpy_config_aneg,
 578                .aneg_done      = genphy_c45_aneg_done,
 579                .read_status    = gpy_read_status,
 580                .config_intr    = gpy_config_intr,
 581                .handle_interrupt = gpy_handle_interrupt,
 582                .set_wol        = gpy_set_wol,
 583                .get_wol        = gpy_get_wol,
 584                .set_loopback   = gpy_loopback,
 585        },
 586        {
 587                PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
 588                .name           = "Maxlinear Ethernet GPY211C",
 589                .get_features   = genphy_c45_pma_read_abilities,
 590                .config_init    = gpy_config_init,
 591                .probe          = gpy_probe,
 592                .suspend        = genphy_suspend,
 593                .resume         = genphy_resume,
 594                .config_aneg    = gpy_config_aneg,
 595                .aneg_done      = genphy_c45_aneg_done,
 596                .read_status    = gpy_read_status,
 597                .config_intr    = gpy_config_intr,
 598                .handle_interrupt = gpy_handle_interrupt,
 599                .set_wol        = gpy_set_wol,
 600                .get_wol        = gpy_get_wol,
 601                .set_loopback   = gpy_loopback,
 602        },
 603        {
 604                .phy_id         = PHY_ID_GPY212B,
 605                .phy_id_mask    = PHY_ID_GPY21xB_MASK,
 606                .name           = "Maxlinear Ethernet GPY212B",
 607                .get_features   = genphy_c45_pma_read_abilities,
 608                .config_init    = gpy_config_init,
 609                .probe          = gpy_probe,
 610                .suspend        = genphy_suspend,
 611                .resume         = genphy_resume,
 612                .config_aneg    = gpy_config_aneg,
 613                .aneg_done      = genphy_c45_aneg_done,
 614                .read_status    = gpy_read_status,
 615                .config_intr    = gpy_config_intr,
 616                .handle_interrupt = gpy_handle_interrupt,
 617                .set_wol        = gpy_set_wol,
 618                .get_wol        = gpy_get_wol,
 619                .set_loopback   = gpy_loopback,
 620        },
 621        {
 622                PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
 623                .name           = "Maxlinear Ethernet GPY212C",
 624                .get_features   = genphy_c45_pma_read_abilities,
 625                .config_init    = gpy_config_init,
 626                .probe          = gpy_probe,
 627                .suspend        = genphy_suspend,
 628                .resume         = genphy_resume,
 629                .config_aneg    = gpy_config_aneg,
 630                .aneg_done      = genphy_c45_aneg_done,
 631                .read_status    = gpy_read_status,
 632                .config_intr    = gpy_config_intr,
 633                .handle_interrupt = gpy_handle_interrupt,
 634                .set_wol        = gpy_set_wol,
 635                .get_wol        = gpy_get_wol,
 636                .set_loopback   = gpy_loopback,
 637        },
 638        {
 639                .phy_id         = PHY_ID_GPY215B,
 640                .phy_id_mask    = PHY_ID_GPYx15B_MASK,
 641                .name           = "Maxlinear Ethernet GPY215B",
 642                .get_features   = genphy_c45_pma_read_abilities,
 643                .config_init    = gpy_config_init,
 644                .probe          = gpy_probe,
 645                .suspend        = genphy_suspend,
 646                .resume         = genphy_resume,
 647                .config_aneg    = gpy_config_aneg,
 648                .aneg_done      = genphy_c45_aneg_done,
 649                .read_status    = gpy_read_status,
 650                .config_intr    = gpy_config_intr,
 651                .handle_interrupt = gpy_handle_interrupt,
 652                .set_wol        = gpy_set_wol,
 653                .get_wol        = gpy_get_wol,
 654                .set_loopback   = gpy_loopback,
 655        },
 656        {
 657                PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
 658                .name           = "Maxlinear Ethernet GPY215C",
 659                .get_features   = genphy_c45_pma_read_abilities,
 660                .config_init    = gpy_config_init,
 661                .probe          = gpy_probe,
 662                .suspend        = genphy_suspend,
 663                .resume         = genphy_resume,
 664                .config_aneg    = gpy_config_aneg,
 665                .aneg_done      = genphy_c45_aneg_done,
 666                .read_status    = gpy_read_status,
 667                .config_intr    = gpy_config_intr,
 668                .handle_interrupt = gpy_handle_interrupt,
 669                .set_wol        = gpy_set_wol,
 670                .get_wol        = gpy_get_wol,
 671                .set_loopback   = gpy_loopback,
 672        },
 673        {
 674                PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
 675                .name           = "Maxlinear Ethernet GPY241B",
 676                .get_features   = genphy_c45_pma_read_abilities,
 677                .config_init    = gpy_config_init,
 678                .probe          = gpy_probe,
 679                .suspend        = genphy_suspend,
 680                .resume         = genphy_resume,
 681                .config_aneg    = gpy_config_aneg,
 682                .aneg_done      = genphy_c45_aneg_done,
 683                .read_status    = gpy_read_status,
 684                .config_intr    = gpy_config_intr,
 685                .handle_interrupt = gpy_handle_interrupt,
 686                .set_wol        = gpy_set_wol,
 687                .get_wol        = gpy_get_wol,
 688                .set_loopback   = gpy_loopback,
 689        },
 690        {
 691                PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
 692                .name           = "Maxlinear Ethernet GPY241BM",
 693                .get_features   = genphy_c45_pma_read_abilities,
 694                .config_init    = gpy_config_init,
 695                .probe          = gpy_probe,
 696                .suspend        = genphy_suspend,
 697                .resume         = genphy_resume,
 698                .config_aneg    = gpy_config_aneg,
 699                .aneg_done      = genphy_c45_aneg_done,
 700                .read_status    = gpy_read_status,
 701                .config_intr    = gpy_config_intr,
 702                .handle_interrupt = gpy_handle_interrupt,
 703                .set_wol        = gpy_set_wol,
 704                .get_wol        = gpy_get_wol,
 705                .set_loopback   = gpy_loopback,
 706        },
 707        {
 708                PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
 709                .name           = "Maxlinear Ethernet GPY245B",
 710                .get_features   = genphy_c45_pma_read_abilities,
 711                .config_init    = gpy_config_init,
 712                .probe          = gpy_probe,
 713                .suspend        = genphy_suspend,
 714                .resume         = genphy_resume,
 715                .config_aneg    = gpy_config_aneg,
 716                .aneg_done      = genphy_c45_aneg_done,
 717                .read_status    = gpy_read_status,
 718                .config_intr    = gpy_config_intr,
 719                .handle_interrupt = gpy_handle_interrupt,
 720                .set_wol        = gpy_set_wol,
 721                .get_wol        = gpy_get_wol,
 722                .set_loopback   = gpy_loopback,
 723        },
 724};
 725module_phy_driver(gpy_drivers);
 726
 727static struct mdio_device_id __maybe_unused gpy_tbl[] = {
 728        {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
 729        {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
 730        {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
 731        {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
 732        {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
 733        {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
 734        {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
 735        {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
 736        {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
 737        {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
 738        {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
 739        {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
 740        { }
 741};
 742MODULE_DEVICE_TABLE(mdio, gpy_tbl);
 743
 744MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
 745MODULE_AUTHOR("Xu Liang");
 746MODULE_LICENSE("GPL");
 747