linux/drivers/net/phy/bcm7xxx.c
<<
>>
Prefs
   1/*
   2 * Broadcom BCM7xxx internal transceivers support.
   3 *
   4 * Copyright (C) 2014-2017 Broadcom
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/phy.h>
  14#include <linux/delay.h>
  15#include "bcm-phy-lib.h"
  16#include <linux/bitops.h>
  17#include <linux/brcmphy.h>
  18#include <linux/mdio.h>
  19
  20/* Broadcom BCM7xxx internal PHY registers */
  21
  22/* EPHY only register definitions */
  23#define MII_BCM7XXX_100TX_AUX_CTL       0x10
  24#define MII_BCM7XXX_100TX_FALSE_CAR     0x13
  25#define MII_BCM7XXX_100TX_DISC          0x14
  26#define MII_BCM7XXX_AUX_MODE            0x1d
  27#define  MII_BCM7XXX_64CLK_MDIO         BIT(12)
  28#define MII_BCM7XXX_TEST                0x1f
  29#define  MII_BCM7XXX_SHD_MODE_2         BIT(2)
  30#define MII_BCM7XXX_SHD_2_ADDR_CTRL     0xe
  31#define MII_BCM7XXX_SHD_2_CTRL_STAT     0xf
  32#define MII_BCM7XXX_SHD_2_BIAS_TRIM     0x1a
  33#define MII_BCM7XXX_SHD_3_AN_EEE_ADV    0x3
  34#define MII_BCM7XXX_SHD_3_PCS_CTRL_2    0x6
  35#define  MII_BCM7XXX_PCS_CTRL_2_DEF     0x4400
  36#define MII_BCM7XXX_SHD_3_AN_STAT       0xb
  37#define  MII_BCM7XXX_AN_NULL_MSG_EN     BIT(0)
  38#define  MII_BCM7XXX_AN_EEE_EN          BIT(1)
  39#define MII_BCM7XXX_SHD_3_EEE_THRESH    0xe
  40#define  MII_BCM7XXX_EEE_THRESH_DEF     0x50
  41#define MII_BCM7XXX_SHD_3_TL4           0x23
  42#define  MII_BCM7XXX_TL4_RST_MSK        (BIT(2) | BIT(1))
  43
  44/* 28nm only register definitions */
  45#define MISC_ADDR(base, channel)        base, channel
  46
  47#define DSP_TAP10                       MISC_ADDR(0x0a, 0)
  48#define PLL_PLLCTRL_1                   MISC_ADDR(0x32, 1)
  49#define PLL_PLLCTRL_2                   MISC_ADDR(0x32, 2)
  50#define PLL_PLLCTRL_4                   MISC_ADDR(0x33, 0)
  51
  52#define AFE_RXCONFIG_0                  MISC_ADDR(0x38, 0)
  53#define AFE_RXCONFIG_1                  MISC_ADDR(0x38, 1)
  54#define AFE_RXCONFIG_2                  MISC_ADDR(0x38, 2)
  55#define AFE_RX_LP_COUNTER               MISC_ADDR(0x38, 3)
  56#define AFE_TX_CONFIG                   MISC_ADDR(0x39, 0)
  57#define AFE_VDCA_ICTRL_0                MISC_ADDR(0x39, 1)
  58#define AFE_VDAC_OTHERS_0               MISC_ADDR(0x39, 3)
  59#define AFE_HPF_TRIM_OTHERS             MISC_ADDR(0x3a, 0)
  60
  61struct bcm7xxx_phy_priv {
  62        u64     *stats;
  63};
  64
  65static void r_rc_cal_reset(struct phy_device *phydev)
  66{
  67        /* Reset R_CAL/RC_CAL Engine */
  68        bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
  69
  70        /* Disable Reset R_AL/RC_CAL Engine */
  71        bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
  72}
  73
  74static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
  75{
  76        /* Increase VCO range to prevent unlocking problem of PLL at low
  77         * temp
  78         */
  79        bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
  80
  81        /* Change Ki to 011 */
  82        bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
  83
  84        /* Disable loading of TVCO buffer to bandgap, set bandgap trim
  85         * to 111
  86         */
  87        bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
  88
  89        /* Adjust bias current trim by -3 */
  90        bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
  91
  92        /* Switch to CORE_BASE1E */
  93        phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
  94
  95        r_rc_cal_reset(phydev);
  96
  97        /* write AFE_RXCONFIG_0 */
  98        bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
  99
 100        /* write AFE_RXCONFIG_1 */
 101        bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
 102
 103        /* write AFE_RX_LP_COUNTER */
 104        bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
 105
 106        /* write AFE_HPF_TRIM_OTHERS */
 107        bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
 108
 109        /* write AFTE_TX_CONFIG */
 110        bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
 111
 112        return 0;
 113}
 114
 115static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
 116{
 117        /* AFE_RXCONFIG_0 */
 118        bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
 119
 120        /* AFE_RXCONFIG_1 */
 121        bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
 122
 123        /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
 124        bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
 125
 126        /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
 127        bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
 128
 129        /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
 130        bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
 131
 132        /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
 133        bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
 134
 135        /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
 136        bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
 137
 138        /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
 139         * offset for HT=0 code
 140         */
 141        bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
 142
 143        /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
 144        phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
 145
 146        /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
 147        bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
 148
 149        /* Reset R_CAL/RC_CAL engine */
 150        r_rc_cal_reset(phydev);
 151
 152        return 0;
 153}
 154
 155static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
 156{
 157        /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
 158        bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
 159
 160        /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
 161        bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
 162
 163        /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
 164        bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
 165
 166        /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
 167         * offset for HT=0 code
 168         */
 169        bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
 170
 171        /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
 172        phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
 173
 174        /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
 175        bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
 176
 177        /* Reset R_CAL/RC_CAL engine */
 178        r_rc_cal_reset(phydev);
 179
 180        return 0;
 181}
 182
 183static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)
 184{
 185        /* +1 RC_CAL codes for RL centering for both LT and HT conditions */
 186        bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);
 187
 188        /* Cut master bias current by 2% to compensate for RC_CAL offset */
 189        bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);
 190
 191        /* Improve hybrid leakage */
 192        bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);
 193
 194        /* Change rx_on_tune 8 to 0xf */
 195        bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);
 196
 197        /* Change 100Tx EEE bandwidth */
 198        bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);
 199
 200        /* Enable ffe zero detection for Vitesse interoperability */
 201        bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);
 202
 203        r_rc_cal_reset(phydev);
 204
 205        return 0;
 206}
 207
 208static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 209{
 210        u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
 211        u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
 212        u8 count;
 213        int ret = 0;
 214
 215        /* Newer devices have moved the revision information back into a
 216         * standard location in MII_PHYS_ID[23]
 217         */
 218        if (rev == 0)
 219                rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
 220
 221        pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
 222                     phydev_name(phydev), phydev->drv->name, rev, patch);
 223
 224        /* Dummy read to a register to workaround an issue upon reset where the
 225         * internal inverter may not allow the first MDIO transaction to pass
 226         * the MDIO management controller and make us return 0xffff for such
 227         * reads.
 228         */
 229        phy_read(phydev, MII_BMSR);
 230
 231        switch (rev) {
 232        case 0xa0:
 233        case 0xb0:
 234                ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
 235                break;
 236        case 0xd0:
 237                ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
 238                break;
 239        case 0xe0:
 240        case 0xf0:
 241        /* Rev G0 introduces a roll over */
 242        case 0x10:
 243                ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
 244                break;
 245        case 0x01:
 246                ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
 247                break;
 248        default:
 249                break;
 250        }
 251
 252        if (ret)
 253                return ret;
 254
 255        ret = bcm_phy_downshift_get(phydev, &count);
 256        if (ret)
 257                return ret;
 258
 259        /* Only enable EEE if Wirespeed/downshift is disabled */
 260        ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
 261        if (ret)
 262                return ret;
 263
 264        return bcm_phy_enable_apd(phydev, true);
 265}
 266
 267static int bcm7xxx_28nm_resume(struct phy_device *phydev)
 268{
 269        int ret;
 270
 271        /* Re-apply workarounds coming out suspend/resume */
 272        ret = bcm7xxx_28nm_config_init(phydev);
 273        if (ret)
 274                return ret;
 275
 276        /* 28nm Gigabit PHYs come out of reset without any half-duplex
 277         * or "hub" compliant advertised mode, fix that. This does not
 278         * cause any problems with the PHY library since genphy_config_aneg()
 279         * gracefully handles auto-negotiated and forced modes.
 280         */
 281        return genphy_config_aneg(phydev);
 282}
 283
 284static int phy_set_clr_bits(struct phy_device *dev, int location,
 285                                        int set_mask, int clr_mask)
 286{
 287        int v, ret;
 288
 289        v = phy_read(dev, location);
 290        if (v < 0)
 291                return v;
 292
 293        v &= ~clr_mask;
 294        v |= set_mask;
 295
 296        ret = phy_write(dev, location, v);
 297        if (ret < 0)
 298                return ret;
 299
 300        return v;
 301}
 302
 303static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev)
 304{
 305        int ret;
 306
 307        /* set shadow mode 2 */
 308        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
 309                               MII_BCM7XXX_SHD_MODE_2, 0);
 310        if (ret < 0)
 311                return ret;
 312
 313        /* Set current trim values INT_trim = -1, Ext_trim =0 */
 314        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, 0x3BE0);
 315        if (ret < 0)
 316                goto reset_shadow_mode;
 317
 318        /* Cal reset */
 319        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 320                        MII_BCM7XXX_SHD_3_TL4);
 321        if (ret < 0)
 322                goto reset_shadow_mode;
 323        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 324                               MII_BCM7XXX_TL4_RST_MSK, 0);
 325        if (ret < 0)
 326                goto reset_shadow_mode;
 327
 328        /* Cal reset disable */
 329        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 330                        MII_BCM7XXX_SHD_3_TL4);
 331        if (ret < 0)
 332                goto reset_shadow_mode;
 333        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 334                               0, MII_BCM7XXX_TL4_RST_MSK);
 335        if (ret < 0)
 336                goto reset_shadow_mode;
 337
 338reset_shadow_mode:
 339        /* reset shadow mode 2 */
 340        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,
 341                               MII_BCM7XXX_SHD_MODE_2);
 342        if (ret < 0)
 343                return ret;
 344
 345        return 0;
 346}
 347
 348/* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */
 349static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev)
 350{
 351        int ret;
 352
 353        /* set shadow mode 1 */
 354        ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST,
 355                               MII_BRCM_FET_BT_SRE, 0);
 356        if (ret < 0)
 357                return ret;
 358
 359        /* Enable auto-power down */
 360        ret = phy_set_clr_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
 361                               MII_BRCM_FET_SHDW_AS2_APDE, 0);
 362        if (ret < 0)
 363                return ret;
 364
 365        /* reset shadow mode 1 */
 366        ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, 0,
 367                               MII_BRCM_FET_BT_SRE);
 368        if (ret < 0)
 369                return ret;
 370
 371        return 0;
 372}
 373
 374static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev)
 375{
 376        int ret;
 377
 378        /* set shadow mode 2 */
 379        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
 380                               MII_BCM7XXX_SHD_MODE_2, 0);
 381        if (ret < 0)
 382                return ret;
 383
 384        /* Advertise supported modes */
 385        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 386                        MII_BCM7XXX_SHD_3_AN_EEE_ADV);
 387        if (ret < 0)
 388                goto reset_shadow_mode;
 389        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 390                        MDIO_EEE_100TX);
 391        if (ret < 0)
 392                goto reset_shadow_mode;
 393
 394        /* Restore Defaults */
 395        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 396                        MII_BCM7XXX_SHD_3_PCS_CTRL_2);
 397        if (ret < 0)
 398                goto reset_shadow_mode;
 399        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 400                        MII_BCM7XXX_PCS_CTRL_2_DEF);
 401        if (ret < 0)
 402                goto reset_shadow_mode;
 403
 404        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 405                        MII_BCM7XXX_SHD_3_EEE_THRESH);
 406        if (ret < 0)
 407                goto reset_shadow_mode;
 408        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 409                        MII_BCM7XXX_EEE_THRESH_DEF);
 410        if (ret < 0)
 411                goto reset_shadow_mode;
 412
 413        /* Enable EEE autonegotiation */
 414        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
 415                        MII_BCM7XXX_SHD_3_AN_STAT);
 416        if (ret < 0)
 417                goto reset_shadow_mode;
 418        ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
 419                        (MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN));
 420        if (ret < 0)
 421                goto reset_shadow_mode;
 422
 423reset_shadow_mode:
 424        /* reset shadow mode 2 */
 425        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,
 426                               MII_BCM7XXX_SHD_MODE_2);
 427        if (ret < 0)
 428                return ret;
 429
 430        /* Restart autoneg */
 431        phy_write(phydev, MII_BMCR,
 432                  (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART));
 433
 434        return 0;
 435}
 436
 437static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev)
 438{
 439        u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
 440        int ret = 0;
 441
 442        pr_info_once("%s: %s PHY revision: 0x%02x\n",
 443                     phydev_name(phydev), phydev->drv->name, rev);
 444
 445        /* Dummy read to a register to workaround a possible issue upon reset
 446         * where the internal inverter may not allow the first MDIO transaction
 447         * to pass the MDIO management controller and make us return 0xffff for
 448         * such reads.
 449         */
 450        phy_read(phydev, MII_BMSR);
 451
 452        /* Apply AFE software work-around if necessary */
 453        if (rev == 0x01) {
 454                ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev);
 455                if (ret)
 456                        return ret;
 457        }
 458
 459        ret = bcm7xxx_28nm_ephy_eee_enable(phydev);
 460        if (ret)
 461                return ret;
 462
 463        return bcm7xxx_28nm_ephy_apd_enable(phydev);
 464}
 465
 466static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev)
 467{
 468        int ret;
 469
 470        /* Re-apply workarounds coming out suspend/resume */
 471        ret = bcm7xxx_28nm_ephy_config_init(phydev);
 472        if (ret)
 473                return ret;
 474
 475        return genphy_config_aneg(phydev);
 476}
 477
 478static int bcm7xxx_config_init(struct phy_device *phydev)
 479{
 480        int ret;
 481
 482        /* Enable 64 clock MDIO */
 483        phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);
 484        phy_read(phydev, MII_BCM7XXX_AUX_MODE);
 485
 486        /* set shadow mode 2 */
 487        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
 488                        MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
 489        if (ret < 0)
 490                return ret;
 491
 492        /* set iddq_clkbias */
 493        phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
 494        udelay(10);
 495
 496        /* reset iddq_clkbias */
 497        phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
 498
 499        phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
 500
 501        /* reset shadow mode 2 */
 502        ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);
 503        if (ret < 0)
 504                return ret;
 505
 506        return 0;
 507}
 508
 509/* Workaround for putting the PHY in IDDQ mode, required
 510 * for all BCM7XXX 40nm and 65nm PHYs
 511 */
 512static int bcm7xxx_suspend(struct phy_device *phydev)
 513{
 514        int ret;
 515        static const struct bcm7xxx_regs {
 516                int reg;
 517                u16 value;
 518        } bcm7xxx_suspend_cfg[] = {
 519                { MII_BCM7XXX_TEST, 0x008b },
 520                { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
 521                { MII_BCM7XXX_100TX_DISC, 0x7000 },
 522                { MII_BCM7XXX_TEST, 0x000f },
 523                { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
 524                { MII_BCM7XXX_TEST, 0x000b },
 525        };
 526        unsigned int i;
 527
 528        for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
 529                ret = phy_write(phydev,
 530                                bcm7xxx_suspend_cfg[i].reg,
 531                                bcm7xxx_suspend_cfg[i].value);
 532                if (ret)
 533                        return ret;
 534        }
 535
 536        return 0;
 537}
 538
 539static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
 540                                    struct ethtool_tunable *tuna,
 541                                    void *data)
 542{
 543        switch (tuna->id) {
 544        case ETHTOOL_PHY_DOWNSHIFT:
 545                return bcm_phy_downshift_get(phydev, (u8 *)data);
 546        default:
 547                return -EOPNOTSUPP;
 548        }
 549}
 550
 551static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
 552                                    struct ethtool_tunable *tuna,
 553                                    const void *data)
 554{
 555        u8 count = *(u8 *)data;
 556        int ret;
 557
 558        switch (tuna->id) {
 559        case ETHTOOL_PHY_DOWNSHIFT:
 560                ret = bcm_phy_downshift_set(phydev, count);
 561                break;
 562        default:
 563                return -EOPNOTSUPP;
 564        }
 565
 566        if (ret)
 567                return ret;
 568
 569        /* Disable EEE advertisement since this prevents the PHY
 570         * from successfully linking up, trigger auto-negotiation restart
 571         * to let the MAC decide what to do.
 572         */
 573        ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
 574        if (ret)
 575                return ret;
 576
 577        return genphy_restart_aneg(phydev);
 578}
 579
 580static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
 581                                       struct ethtool_stats *stats, u64 *data)
 582{
 583        struct bcm7xxx_phy_priv *priv = phydev->priv;
 584
 585        bcm_phy_get_stats(phydev, priv->stats, stats, data);
 586}
 587
 588static int bcm7xxx_28nm_probe(struct phy_device *phydev)
 589{
 590        struct bcm7xxx_phy_priv *priv;
 591
 592        priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
 593        if (!priv)
 594                return -ENOMEM;
 595
 596        phydev->priv = priv;
 597
 598        priv->stats = devm_kcalloc(&phydev->mdio.dev,
 599                                   bcm_phy_get_sset_count(phydev), sizeof(u64),
 600                                   GFP_KERNEL);
 601        if (!priv->stats)
 602                return -ENOMEM;
 603
 604        return 0;
 605}
 606
 607#define BCM7XXX_28NM_GPHY(_oui, _name)                                  \
 608{                                                                       \
 609        .phy_id         = (_oui),                                       \
 610        .phy_id_mask    = 0xfffffff0,                                   \
 611        .name           = _name,                                        \
 612        .features       = PHY_GBIT_FEATURES,                            \
 613        .flags          = PHY_IS_INTERNAL,                              \
 614        .config_init    = bcm7xxx_28nm_config_init,                     \
 615        .resume         = bcm7xxx_28nm_resume,                          \
 616        .get_tunable    = bcm7xxx_28nm_get_tunable,                     \
 617        .set_tunable    = bcm7xxx_28nm_set_tunable,                     \
 618        .get_sset_count = bcm_phy_get_sset_count,                       \
 619        .get_strings    = bcm_phy_get_strings,                          \
 620        .get_stats      = bcm7xxx_28nm_get_phy_stats,                   \
 621        .probe          = bcm7xxx_28nm_probe,                           \
 622}
 623
 624#define BCM7XXX_28NM_EPHY(_oui, _name)                                  \
 625{                                                                       \
 626        .phy_id         = (_oui),                                       \
 627        .phy_id_mask    = 0xfffffff0,                                   \
 628        .name           = _name,                                        \
 629        .features       = PHY_BASIC_FEATURES,                           \
 630        .flags          = PHY_IS_INTERNAL,                              \
 631        .config_init    = bcm7xxx_28nm_ephy_config_init,                \
 632        .resume         = bcm7xxx_28nm_ephy_resume,                     \
 633        .get_sset_count = bcm_phy_get_sset_count,                       \
 634        .get_strings    = bcm_phy_get_strings,                          \
 635        .get_stats      = bcm7xxx_28nm_get_phy_stats,                   \
 636        .probe          = bcm7xxx_28nm_probe,                           \
 637}
 638
 639#define BCM7XXX_40NM_EPHY(_oui, _name)                                  \
 640{                                                                       \
 641        .phy_id         = (_oui),                                       \
 642        .phy_id_mask    = 0xfffffff0,                                   \
 643        .name           = _name,                                        \
 644        .features       = PHY_BASIC_FEATURES,                           \
 645        .flags          = PHY_IS_INTERNAL,                              \
 646        .config_init    = bcm7xxx_config_init,                          \
 647        .suspend        = bcm7xxx_suspend,                              \
 648        .resume         = bcm7xxx_config_init,                          \
 649}
 650
 651static struct phy_driver bcm7xxx_driver[] = {
 652        BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
 653        BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"),
 654        BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"),
 655        BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"),
 656        BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"),
 657        BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),
 658        BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
 659        BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
 660        BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"),
 661        BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
 662        BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
 663        BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
 664        BCM7XXX_28NM_GPHY(PHY_ID_BCM_OMEGA, "Broadcom Omega Combo GPHY"),
 665        BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),
 666        BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),
 667        BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),
 668        BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),
 669        BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),
 670};
 671
 672static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
 673        { PHY_ID_BCM7250, 0xfffffff0, },
 674        { PHY_ID_BCM7255, 0xfffffff0, },
 675        { PHY_ID_BCM7260, 0xfffffff0, },
 676        { PHY_ID_BCM7268, 0xfffffff0, },
 677        { PHY_ID_BCM7271, 0xfffffff0, },
 678        { PHY_ID_BCM7278, 0xfffffff0, },
 679        { PHY_ID_BCM7364, 0xfffffff0, },
 680        { PHY_ID_BCM7366, 0xfffffff0, },
 681        { PHY_ID_BCM7346, 0xfffffff0, },
 682        { PHY_ID_BCM7362, 0xfffffff0, },
 683        { PHY_ID_BCM7425, 0xfffffff0, },
 684        { PHY_ID_BCM7429, 0xfffffff0, },
 685        { PHY_ID_BCM74371, 0xfffffff0, },
 686        { PHY_ID_BCM7439, 0xfffffff0, },
 687        { PHY_ID_BCM7435, 0xfffffff0, },
 688        { PHY_ID_BCM7445, 0xfffffff0, },
 689        { }
 690};
 691
 692module_phy_driver(bcm7xxx_driver);
 693
 694MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
 695
 696MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
 697MODULE_LICENSE("GPL");
 698MODULE_AUTHOR("Broadcom Corporation");
 699