linux/drivers/net/phy/broadcom.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *      drivers/net/phy/broadcom.c
   4 *
   5 *      Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
   6 *      transceivers.
   7 *
   8 *      Copyright (c) 2006  Maciej W. Rozycki
   9 *
  10 *      Inspired by code written by Amy Fong.
  11 */
  12
  13#include "bcm-phy-lib.h"
  14#include <linux/module.h>
  15#include <linux/phy.h>
  16#include <linux/brcmphy.h>
  17#include <linux/of.h>
  18
  19#define BRCM_PHY_MODEL(phydev) \
  20        ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
  21
  22#define BRCM_PHY_REV(phydev) \
  23        ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
  24
  25MODULE_DESCRIPTION("Broadcom PHY driver");
  26MODULE_AUTHOR("Maciej W. Rozycki");
  27MODULE_LICENSE("GPL");
  28
  29static int bcm54210e_config_init(struct phy_device *phydev)
  30{
  31        int val;
  32
  33        val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  34        val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
  35        val |= MII_BCM54XX_AUXCTL_MISC_WREN;
  36        bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val);
  37
  38        val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
  39        val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
  40        bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
  41
  42        if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
  43                val = phy_read(phydev, MII_CTRL1000);
  44                val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
  45                phy_write(phydev, MII_CTRL1000, val);
  46        }
  47
  48        return 0;
  49}
  50
  51static int bcm54612e_config_init(struct phy_device *phydev)
  52{
  53        int reg;
  54
  55        /* Clear TX internal delay unless requested. */
  56        if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
  57            (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
  58                /* Disable TXD to GTXCLK clock delay (default set) */
  59                /* Bit 9 is the only field in shadow register 00011 */
  60                bcm_phy_write_shadow(phydev, 0x03, 0);
  61        }
  62
  63        /* Clear RX internal delay unless requested. */
  64        if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
  65            (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
  66                reg = bcm54xx_auxctl_read(phydev,
  67                                          MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  68                /* Disable RXD to RXC delay (default set) */
  69                reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
  70                /* Clear shadow selector field */
  71                reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
  72                bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
  73                                     MII_BCM54XX_AUXCTL_MISC_WREN | reg);
  74        }
  75
  76        /* Enable CLK125 MUX on LED4 if ref clock is enabled. */
  77        if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
  78                int err;
  79
  80                reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
  81                err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
  82                                        BCM54612E_LED4_CLK125OUT_EN | reg);
  83
  84                if (err < 0)
  85                        return err;
  86        }
  87
  88        return 0;
  89}
  90
  91static int bcm54xx_config_clock_delay(struct phy_device *phydev)
  92{
  93        int rc, val;
  94
  95        /* handling PHY's internal RX clock delay */
  96        val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  97        val |= MII_BCM54XX_AUXCTL_MISC_WREN;
  98        if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
  99            phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 100                /* Disable RGMII RXC-RXD skew */
 101                val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
 102        }
 103        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
 104            phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
 105                /* Enable RGMII RXC-RXD skew */
 106                val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
 107        }
 108        rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
 109                                  val);
 110        if (rc < 0)
 111                return rc;
 112
 113        /* handling PHY's internal TX clock delay */
 114        val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
 115        if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
 116            phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
 117                /* Disable internal TX clock delay */
 118                val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
 119        }
 120        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
 121            phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 122                /* Enable internal TX clock delay */
 123                val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
 124        }
 125        rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
 126        if (rc < 0)
 127                return rc;
 128
 129        return 0;
 130}
 131
 132/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
 133static int bcm50610_a0_workaround(struct phy_device *phydev)
 134{
 135        int err;
 136
 137        err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
 138                                MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
 139                                MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
 140        if (err < 0)
 141                return err;
 142
 143        err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
 144                                MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
 145        if (err < 0)
 146                return err;
 147
 148        err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
 149                                MII_BCM54XX_EXP_EXP75_VDACCTRL);
 150        if (err < 0)
 151                return err;
 152
 153        err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
 154                                MII_BCM54XX_EXP_EXP96_MYST);
 155        if (err < 0)
 156                return err;
 157
 158        err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
 159                                MII_BCM54XX_EXP_EXP97_MYST);
 160
 161        return err;
 162}
 163
 164static int bcm54xx_phydsp_config(struct phy_device *phydev)
 165{
 166        int err, err2;
 167
 168        /* Enable the SMDSP clock */
 169        err = bcm54xx_auxctl_write(phydev,
 170                                   MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
 171                                   MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
 172                                   MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
 173        if (err < 0)
 174                return err;
 175
 176        if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
 177            BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
 178                /* Clear bit 9 to fix a phy interop issue. */
 179                err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
 180                                        MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
 181                if (err < 0)
 182                        goto error;
 183
 184                if (phydev->drv->phy_id == PHY_ID_BCM50610) {
 185                        err = bcm50610_a0_workaround(phydev);
 186                        if (err < 0)
 187                                goto error;
 188                }
 189        }
 190
 191        if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
 192                int val;
 193
 194                val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
 195                if (val < 0)
 196                        goto error;
 197
 198                val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
 199                err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
 200        }
 201
 202error:
 203        /* Disable the SMDSP clock */
 204        err2 = bcm54xx_auxctl_write(phydev,
 205                                    MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
 206                                    MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
 207
 208        /* Return the first error reported. */
 209        return err ? err : err2;
 210}
 211
 212static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
 213{
 214        u32 orig;
 215        int val;
 216        bool clk125en = true;
 217
 218        /* Abort if we are using an untested phy. */
 219        if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
 220            BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
 221            BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
 222                return;
 223
 224        val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
 225        if (val < 0)
 226                return;
 227
 228        orig = val;
 229
 230        if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
 231             BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
 232            BRCM_PHY_REV(phydev) >= 0x3) {
 233                /*
 234                 * Here, bit 0 _disables_ CLK125 when set.
 235                 * This bit is set by default.
 236                 */
 237                clk125en = false;
 238        } else {
 239                if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
 240                        /* Here, bit 0 _enables_ CLK125 when set */
 241                        val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
 242                        clk125en = false;
 243                }
 244        }
 245
 246        if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
 247                val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
 248        else
 249                val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
 250
 251        if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
 252                val |= BCM54XX_SHD_SCR3_TRDDAPD;
 253
 254        if (orig != val)
 255                bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
 256
 257        val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
 258        if (val < 0)
 259                return;
 260
 261        orig = val;
 262
 263        if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
 264                val |= BCM54XX_SHD_APD_EN;
 265        else
 266                val &= ~BCM54XX_SHD_APD_EN;
 267
 268        if (orig != val)
 269                bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
 270}
 271
 272static int bcm54xx_config_init(struct phy_device *phydev)
 273{
 274        int reg, err, val;
 275
 276        reg = phy_read(phydev, MII_BCM54XX_ECR);
 277        if (reg < 0)
 278                return reg;
 279
 280        /* Mask interrupts globally.  */
 281        reg |= MII_BCM54XX_ECR_IM;
 282        err = phy_write(phydev, MII_BCM54XX_ECR, reg);
 283        if (err < 0)
 284                return err;
 285
 286        /* Unmask events we are interested in.  */
 287        reg = ~(MII_BCM54XX_INT_DUPLEX |
 288                MII_BCM54XX_INT_SPEED |
 289                MII_BCM54XX_INT_LINK);
 290        err = phy_write(phydev, MII_BCM54XX_IMR, reg);
 291        if (err < 0)
 292                return err;
 293
 294        if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
 295             BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
 296            (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
 297                bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
 298
 299        if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
 300            (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
 301            (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
 302                bcm54xx_adjust_rxrefclk(phydev);
 303
 304        if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) {
 305                err = bcm54210e_config_init(phydev);
 306                if (err)
 307                        return err;
 308        } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54612E) {
 309                err = bcm54612e_config_init(phydev);
 310                if (err)
 311                        return err;
 312        } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
 313                /* For BCM54810, we need to disable BroadR-Reach function */
 314                val = bcm_phy_read_exp(phydev,
 315                                       BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
 316                val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
 317                err = bcm_phy_write_exp(phydev,
 318                                        BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
 319                                        val);
 320                if (err < 0)
 321                        return err;
 322        }
 323
 324        bcm54xx_phydsp_config(phydev);
 325
 326        /* Encode link speed into LED1 and LED3 pair (green/amber).
 327         * Also flash these two LEDs on activity. This means configuring
 328         * them for MULTICOLOR and encoding link/activity into them.
 329         */
 330        val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
 331                BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
 332        bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val);
 333
 334        val = BCM_LED_MULTICOLOR_IN_PHASE |
 335                BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
 336                BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
 337        bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
 338
 339        return 0;
 340}
 341
 342static int bcm5482_config_init(struct phy_device *phydev)
 343{
 344        int err, reg;
 345
 346        err = bcm54xx_config_init(phydev);
 347
 348        if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
 349                /*
 350                 * Enable secondary SerDes and its use as an LED source
 351                 */
 352                reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
 353                bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
 354                                     reg |
 355                                     BCM5482_SHD_SSD_LEDM |
 356                                     BCM5482_SHD_SSD_EN);
 357
 358                /*
 359                 * Enable SGMII slave mode and auto-detection
 360                 */
 361                reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
 362                err = bcm_phy_read_exp(phydev, reg);
 363                if (err < 0)
 364                        return err;
 365                err = bcm_phy_write_exp(phydev, reg, err |
 366                                        BCM5482_SSD_SGMII_SLAVE_EN |
 367                                        BCM5482_SSD_SGMII_SLAVE_AD);
 368                if (err < 0)
 369                        return err;
 370
 371                /*
 372                 * Disable secondary SerDes powerdown
 373                 */
 374                reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
 375                err = bcm_phy_read_exp(phydev, reg);
 376                if (err < 0)
 377                        return err;
 378                err = bcm_phy_write_exp(phydev, reg,
 379                                        err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
 380                if (err < 0)
 381                        return err;
 382
 383                /*
 384                 * Select 1000BASE-X register set (primary SerDes)
 385                 */
 386                reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE);
 387                bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,
 388                                     reg | BCM5482_SHD_MODE_1000BX);
 389
 390                /*
 391                 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
 392                 * (Use LED1 as secondary SerDes ACTIVITY LED)
 393                 */
 394                bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
 395                        BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
 396                        BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
 397
 398                /*
 399                 * Auto-negotiation doesn't seem to work quite right
 400                 * in this mode, so we disable it and force it to the
 401                 * right speed/duplex setting.  Only 'link status'
 402                 * is important.
 403                 */
 404                phydev->autoneg = AUTONEG_DISABLE;
 405                phydev->speed = SPEED_1000;
 406                phydev->duplex = DUPLEX_FULL;
 407        }
 408
 409        return err;
 410}
 411
 412static int bcm5482_read_status(struct phy_device *phydev)
 413{
 414        int err;
 415
 416        err = genphy_read_status(phydev);
 417
 418        if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
 419                /*
 420                 * Only link status matters for 1000Base-X mode, so force
 421                 * 1000 Mbit/s full-duplex status
 422                 */
 423                if (phydev->link) {
 424                        phydev->speed = SPEED_1000;
 425                        phydev->duplex = DUPLEX_FULL;
 426                }
 427        }
 428
 429        return err;
 430}
 431
 432static int bcm5481_config_aneg(struct phy_device *phydev)
 433{
 434        struct device_node *np = phydev->mdio.dev.of_node;
 435        int ret;
 436
 437        /* Aneg firsly. */
 438        ret = genphy_config_aneg(phydev);
 439
 440        /* Then we can set up the delay. */
 441        bcm54xx_config_clock_delay(phydev);
 442
 443        if (of_property_read_bool(np, "enet-phy-lane-swap")) {
 444                /* Lane Swap - Undocumented register...magic! */
 445                ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
 446                                        0x11B);
 447                if (ret < 0)
 448                        return ret;
 449        }
 450
 451        return ret;
 452}
 453
 454static int bcm54616s_config_aneg(struct phy_device *phydev)
 455{
 456        int ret;
 457
 458        /* Aneg firsly. */
 459        ret = genphy_config_aneg(phydev);
 460
 461        /* Then we can set up the delay. */
 462        bcm54xx_config_clock_delay(phydev);
 463
 464        return ret;
 465}
 466
 467static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
 468{
 469        int val;
 470
 471        val = phy_read(phydev, reg);
 472        if (val < 0)
 473                return val;
 474
 475        return phy_write(phydev, reg, val | set);
 476}
 477
 478static int brcm_fet_config_init(struct phy_device *phydev)
 479{
 480        int reg, err, err2, brcmtest;
 481
 482        /* Reset the PHY to bring it to a known state. */
 483        err = phy_write(phydev, MII_BMCR, BMCR_RESET);
 484        if (err < 0)
 485                return err;
 486
 487        reg = phy_read(phydev, MII_BRCM_FET_INTREG);
 488        if (reg < 0)
 489                return reg;
 490
 491        /* Unmask events we are interested in and mask interrupts globally. */
 492        reg = MII_BRCM_FET_IR_DUPLEX_EN |
 493              MII_BRCM_FET_IR_SPEED_EN |
 494              MII_BRCM_FET_IR_LINK_EN |
 495              MII_BRCM_FET_IR_ENABLE |
 496              MII_BRCM_FET_IR_MASK;
 497
 498        err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
 499        if (err < 0)
 500                return err;
 501
 502        /* Enable shadow register access */
 503        brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
 504        if (brcmtest < 0)
 505                return brcmtest;
 506
 507        reg = brcmtest | MII_BRCM_FET_BT_SRE;
 508
 509        err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
 510        if (err < 0)
 511                return err;
 512
 513        /* Set the LED mode */
 514        reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
 515        if (reg < 0) {
 516                err = reg;
 517                goto done;
 518        }
 519
 520        reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
 521        reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
 522
 523        err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
 524        if (err < 0)
 525                goto done;
 526
 527        /* Enable auto MDIX */
 528        err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
 529                                       MII_BRCM_FET_SHDW_MC_FAME);
 530        if (err < 0)
 531                goto done;
 532
 533        if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
 534                /* Enable auto power down */
 535                err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
 536                                               MII_BRCM_FET_SHDW_AS2_APDE);
 537        }
 538
 539done:
 540        /* Disable shadow register access */
 541        err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
 542        if (!err)
 543                err = err2;
 544
 545        return err;
 546}
 547
 548static int brcm_fet_ack_interrupt(struct phy_device *phydev)
 549{
 550        int reg;
 551
 552        /* Clear pending interrupts.  */
 553        reg = phy_read(phydev, MII_BRCM_FET_INTREG);
 554        if (reg < 0)
 555                return reg;
 556
 557        return 0;
 558}
 559
 560static int brcm_fet_config_intr(struct phy_device *phydev)
 561{
 562        int reg, err;
 563
 564        reg = phy_read(phydev, MII_BRCM_FET_INTREG);
 565        if (reg < 0)
 566                return reg;
 567
 568        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 569                reg &= ~MII_BRCM_FET_IR_MASK;
 570        else
 571                reg |= MII_BRCM_FET_IR_MASK;
 572
 573        err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
 574        return err;
 575}
 576
 577struct bcm53xx_phy_priv {
 578        u64     *stats;
 579};
 580
 581static int bcm53xx_phy_probe(struct phy_device *phydev)
 582{
 583        struct bcm53xx_phy_priv *priv;
 584
 585        priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
 586        if (!priv)
 587                return -ENOMEM;
 588
 589        phydev->priv = priv;
 590
 591        priv->stats = devm_kcalloc(&phydev->mdio.dev,
 592                                   bcm_phy_get_sset_count(phydev), sizeof(u64),
 593                                   GFP_KERNEL);
 594        if (!priv->stats)
 595                return -ENOMEM;
 596
 597        return 0;
 598}
 599
 600static void bcm53xx_phy_get_stats(struct phy_device *phydev,
 601                                  struct ethtool_stats *stats, u64 *data)
 602{
 603        struct bcm53xx_phy_priv *priv = phydev->priv;
 604
 605        bcm_phy_get_stats(phydev, priv->stats, stats, data);
 606}
 607
 608static struct phy_driver broadcom_drivers[] = {
 609{
 610        .phy_id         = PHY_ID_BCM5411,
 611        .phy_id_mask    = 0xfffffff0,
 612        .name           = "Broadcom BCM5411",
 613        /* PHY_GBIT_FEATURES */
 614        .config_init    = bcm54xx_config_init,
 615        .ack_interrupt  = bcm_phy_ack_intr,
 616        .config_intr    = bcm_phy_config_intr,
 617}, {
 618        .phy_id         = PHY_ID_BCM5421,
 619        .phy_id_mask    = 0xfffffff0,
 620        .name           = "Broadcom BCM5421",
 621        /* PHY_GBIT_FEATURES */
 622        .config_init    = bcm54xx_config_init,
 623        .ack_interrupt  = bcm_phy_ack_intr,
 624        .config_intr    = bcm_phy_config_intr,
 625}, {
 626        .phy_id         = PHY_ID_BCM54210E,
 627        .phy_id_mask    = 0xfffffff0,
 628        .name           = "Broadcom BCM54210E",
 629        /* PHY_GBIT_FEATURES */
 630        .config_init    = bcm54xx_config_init,
 631        .ack_interrupt  = bcm_phy_ack_intr,
 632        .config_intr    = bcm_phy_config_intr,
 633}, {
 634        .phy_id         = PHY_ID_BCM5461,
 635        .phy_id_mask    = 0xfffffff0,
 636        .name           = "Broadcom BCM5461",
 637        /* PHY_GBIT_FEATURES */
 638        .config_init    = bcm54xx_config_init,
 639        .ack_interrupt  = bcm_phy_ack_intr,
 640        .config_intr    = bcm_phy_config_intr,
 641}, {
 642        .phy_id         = PHY_ID_BCM54612E,
 643        .phy_id_mask    = 0xfffffff0,
 644        .name           = "Broadcom BCM54612E",
 645        /* PHY_GBIT_FEATURES */
 646        .config_init    = bcm54xx_config_init,
 647        .ack_interrupt  = bcm_phy_ack_intr,
 648        .config_intr    = bcm_phy_config_intr,
 649}, {
 650        .phy_id         = PHY_ID_BCM54616S,
 651        .phy_id_mask    = 0xfffffff0,
 652        .name           = "Broadcom BCM54616S",
 653        /* PHY_GBIT_FEATURES */
 654        .config_init    = bcm54xx_config_init,
 655        .config_aneg    = bcm54616s_config_aneg,
 656        .ack_interrupt  = bcm_phy_ack_intr,
 657        .config_intr    = bcm_phy_config_intr,
 658}, {
 659        .phy_id         = PHY_ID_BCM5464,
 660        .phy_id_mask    = 0xfffffff0,
 661        .name           = "Broadcom BCM5464",
 662        /* PHY_GBIT_FEATURES */
 663        .config_init    = bcm54xx_config_init,
 664        .ack_interrupt  = bcm_phy_ack_intr,
 665        .config_intr    = bcm_phy_config_intr,
 666        .suspend        = genphy_suspend,
 667        .resume         = genphy_resume,
 668}, {
 669        .phy_id         = PHY_ID_BCM5481,
 670        .phy_id_mask    = 0xfffffff0,
 671        .name           = "Broadcom BCM5481",
 672        /* PHY_GBIT_FEATURES */
 673        .config_init    = bcm54xx_config_init,
 674        .config_aneg    = bcm5481_config_aneg,
 675        .ack_interrupt  = bcm_phy_ack_intr,
 676        .config_intr    = bcm_phy_config_intr,
 677}, {
 678        .phy_id         = PHY_ID_BCM54810,
 679        .phy_id_mask    = 0xfffffff0,
 680        .name           = "Broadcom BCM54810",
 681        /* PHY_GBIT_FEATURES */
 682        .config_init    = bcm54xx_config_init,
 683        .config_aneg    = bcm5481_config_aneg,
 684        .ack_interrupt  = bcm_phy_ack_intr,
 685        .config_intr    = bcm_phy_config_intr,
 686}, {
 687        .phy_id         = PHY_ID_BCM5482,
 688        .phy_id_mask    = 0xfffffff0,
 689        .name           = "Broadcom BCM5482",
 690        /* PHY_GBIT_FEATURES */
 691        .config_init    = bcm5482_config_init,
 692        .read_status    = bcm5482_read_status,
 693        .ack_interrupt  = bcm_phy_ack_intr,
 694        .config_intr    = bcm_phy_config_intr,
 695}, {
 696        .phy_id         = PHY_ID_BCM50610,
 697        .phy_id_mask    = 0xfffffff0,
 698        .name           = "Broadcom BCM50610",
 699        /* PHY_GBIT_FEATURES */
 700        .config_init    = bcm54xx_config_init,
 701        .ack_interrupt  = bcm_phy_ack_intr,
 702        .config_intr    = bcm_phy_config_intr,
 703}, {
 704        .phy_id         = PHY_ID_BCM50610M,
 705        .phy_id_mask    = 0xfffffff0,
 706        .name           = "Broadcom BCM50610M",
 707        /* PHY_GBIT_FEATURES */
 708        .config_init    = bcm54xx_config_init,
 709        .ack_interrupt  = bcm_phy_ack_intr,
 710        .config_intr    = bcm_phy_config_intr,
 711}, {
 712        .phy_id         = PHY_ID_BCM57780,
 713        .phy_id_mask    = 0xfffffff0,
 714        .name           = "Broadcom BCM57780",
 715        /* PHY_GBIT_FEATURES */
 716        .config_init    = bcm54xx_config_init,
 717        .ack_interrupt  = bcm_phy_ack_intr,
 718        .config_intr    = bcm_phy_config_intr,
 719}, {
 720        .phy_id         = PHY_ID_BCMAC131,
 721        .phy_id_mask    = 0xfffffff0,
 722        .name           = "Broadcom BCMAC131",
 723        /* PHY_BASIC_FEATURES */
 724        .config_init    = brcm_fet_config_init,
 725        .ack_interrupt  = brcm_fet_ack_interrupt,
 726        .config_intr    = brcm_fet_config_intr,
 727}, {
 728        .phy_id         = PHY_ID_BCM5241,
 729        .phy_id_mask    = 0xfffffff0,
 730        .name           = "Broadcom BCM5241",
 731        /* PHY_BASIC_FEATURES */
 732        .config_init    = brcm_fet_config_init,
 733        .ack_interrupt  = brcm_fet_ack_interrupt,
 734        .config_intr    = brcm_fet_config_intr,
 735}, {
 736        .phy_id         = PHY_ID_BCM5395,
 737        .phy_id_mask    = 0xfffffff0,
 738        .name           = "Broadcom BCM5395",
 739        .flags          = PHY_IS_INTERNAL,
 740        /* PHY_GBIT_FEATURES */
 741        .get_sset_count = bcm_phy_get_sset_count,
 742        .get_strings    = bcm_phy_get_strings,
 743        .get_stats      = bcm53xx_phy_get_stats,
 744        .probe          = bcm53xx_phy_probe,
 745}, {
 746        .phy_id         = PHY_ID_BCM89610,
 747        .phy_id_mask    = 0xfffffff0,
 748        .name           = "Broadcom BCM89610",
 749        /* PHY_GBIT_FEATURES */
 750        .config_init    = bcm54xx_config_init,
 751        .ack_interrupt  = bcm_phy_ack_intr,
 752        .config_intr    = bcm_phy_config_intr,
 753} };
 754
 755module_phy_driver(broadcom_drivers);
 756
 757static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
 758        { PHY_ID_BCM5411, 0xfffffff0 },
 759        { PHY_ID_BCM5421, 0xfffffff0 },
 760        { PHY_ID_BCM54210E, 0xfffffff0 },
 761        { PHY_ID_BCM5461, 0xfffffff0 },
 762        { PHY_ID_BCM54612E, 0xfffffff0 },
 763        { PHY_ID_BCM54616S, 0xfffffff0 },
 764        { PHY_ID_BCM5464, 0xfffffff0 },
 765        { PHY_ID_BCM5481, 0xfffffff0 },
 766        { PHY_ID_BCM54810, 0xfffffff0 },
 767        { PHY_ID_BCM5482, 0xfffffff0 },
 768        { PHY_ID_BCM50610, 0xfffffff0 },
 769        { PHY_ID_BCM50610M, 0xfffffff0 },
 770        { PHY_ID_BCM57780, 0xfffffff0 },
 771        { PHY_ID_BCMAC131, 0xfffffff0 },
 772        { PHY_ID_BCM5241, 0xfffffff0 },
 773        { PHY_ID_BCM5395, 0xfffffff0 },
 774        { PHY_ID_BCM89610, 0xfffffff0 },
 775        { }
 776};
 777
 778MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
 779