root/drivers/net/phy/bcm-phy-lib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bcm_phy_write_exp
  2. bcm_phy_read_exp
  3. bcm54xx_auxctl_read
  4. bcm54xx_auxctl_write
  5. bcm_phy_write_misc
  6. bcm_phy_read_misc
  7. bcm_phy_ack_intr
  8. bcm_phy_config_intr
  9. bcm_phy_read_shadow
  10. bcm_phy_write_shadow
  11. bcm_phy_enable_apd
  12. bcm_phy_set_eee
  13. bcm_phy_downshift_get
  14. bcm_phy_downshift_set
  15. bcm_phy_get_sset_count
  16. bcm_phy_get_strings
  17. bcm_phy_get_stat
  18. bcm_phy_get_stats
  19. bcm_phy_r_rc_cal_reset
  20. bcm_phy_28nm_a0b0_afe_config_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2015-2017 Broadcom
   4  */
   5 
   6 #include "bcm-phy-lib.h"
   7 #include <linux/brcmphy.h>
   8 #include <linux/export.h>
   9 #include <linux/mdio.h>
  10 #include <linux/module.h>
  11 #include <linux/phy.h>
  12 #include <linux/ethtool.h>
  13 
  14 #define MII_BCM_CHANNEL_WIDTH     0x2000
  15 #define BCM_CL45VEN_EEE_ADV       0x3c
  16 
  17 int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
  18 {
  19         int rc;
  20 
  21         rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
  22         if (rc < 0)
  23                 return rc;
  24 
  25         return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
  26 }
  27 EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
  28 
  29 int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
  30 {
  31         int val;
  32 
  33         val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
  34         if (val < 0)
  35                 return val;
  36 
  37         val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
  38 
  39         /* Restore default value.  It's O.K. if this write fails. */
  40         phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
  41 
  42         return val;
  43 }
  44 EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
  45 
  46 int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
  47 {
  48         /* The register must be written to both the Shadow Register Select and
  49          * the Shadow Read Register Selector
  50          */
  51         phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |
  52                   regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
  53         return phy_read(phydev, MII_BCM54XX_AUX_CTL);
  54 }
  55 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
  56 
  57 int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
  58 {
  59         return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
  60 }
  61 EXPORT_SYMBOL(bcm54xx_auxctl_write);
  62 
  63 int bcm_phy_write_misc(struct phy_device *phydev,
  64                        u16 reg, u16 chl, u16 val)
  65 {
  66         int rc;
  67         int tmp;
  68 
  69         rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
  70                        MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  71         if (rc < 0)
  72                 return rc;
  73 
  74         tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
  75         tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
  76         rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
  77         if (rc < 0)
  78                 return rc;
  79 
  80         tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
  81         rc = bcm_phy_write_exp(phydev, tmp, val);
  82 
  83         return rc;
  84 }
  85 EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
  86 
  87 int bcm_phy_read_misc(struct phy_device *phydev,
  88                       u16 reg, u16 chl)
  89 {
  90         int rc;
  91         int tmp;
  92 
  93         rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
  94                        MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  95         if (rc < 0)
  96                 return rc;
  97 
  98         tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
  99         tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
 100         rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
 101         if (rc < 0)
 102                 return rc;
 103 
 104         tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
 105         rc = bcm_phy_read_exp(phydev, tmp);
 106 
 107         return rc;
 108 }
 109 EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
 110 
 111 int bcm_phy_ack_intr(struct phy_device *phydev)
 112 {
 113         int reg;
 114 
 115         /* Clear pending interrupts.  */
 116         reg = phy_read(phydev, MII_BCM54XX_ISR);
 117         if (reg < 0)
 118                 return reg;
 119 
 120         return 0;
 121 }
 122 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
 123 
 124 int bcm_phy_config_intr(struct phy_device *phydev)
 125 {
 126         int reg;
 127 
 128         reg = phy_read(phydev, MII_BCM54XX_ECR);
 129         if (reg < 0)
 130                 return reg;
 131 
 132         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 133                 reg &= ~MII_BCM54XX_ECR_IM;
 134         else
 135                 reg |= MII_BCM54XX_ECR_IM;
 136 
 137         return phy_write(phydev, MII_BCM54XX_ECR, reg);
 138 }
 139 EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
 140 
 141 int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
 142 {
 143         phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
 144         return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
 145 }
 146 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
 147 
 148 int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
 149                          u16 val)
 150 {
 151         return phy_write(phydev, MII_BCM54XX_SHD,
 152                          MII_BCM54XX_SHD_WRITE |
 153                          MII_BCM54XX_SHD_VAL(shadow) |
 154                          MII_BCM54XX_SHD_DATA(val));
 155 }
 156 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
 157 
 158 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
 159 {
 160         int val;
 161 
 162         if (dll_pwr_down) {
 163                 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
 164                 if (val < 0)
 165                         return val;
 166 
 167                 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
 168                 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
 169         }
 170 
 171         val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
 172         if (val < 0)
 173                 return val;
 174 
 175         /* Clear APD bits */
 176         val &= BCM_APD_CLR_MASK;
 177 
 178         if (phydev->autoneg == AUTONEG_ENABLE)
 179                 val |= BCM54XX_SHD_APD_EN;
 180         else
 181                 val |= BCM_NO_ANEG_APD_EN;
 182 
 183         /* Enable energy detect single link pulse for easy wakeup */
 184         val |= BCM_APD_SINGLELP_EN;
 185 
 186         /* Enable Auto Power-Down (APD) for the PHY */
 187         return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
 188 }
 189 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
 190 
 191 int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
 192 {
 193         int val;
 194 
 195         /* Enable EEE at PHY level */
 196         val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);
 197         if (val < 0)
 198                 return val;
 199 
 200         if (enable)
 201                 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
 202         else
 203                 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
 204 
 205         phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);
 206 
 207         /* Advertise EEE */
 208         val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);
 209         if (val < 0)
 210                 return val;
 211 
 212         if (enable)
 213                 val |= (MDIO_EEE_100TX | MDIO_EEE_1000T);
 214         else
 215                 val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T);
 216 
 217         phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);
 218 
 219         return 0;
 220 }
 221 EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
 222 
 223 int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
 224 {
 225         int val;
 226 
 227         val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
 228         if (val < 0)
 229                 return val;
 230 
 231         /* Check if wirespeed is enabled or not */
 232         if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
 233                 *count = DOWNSHIFT_DEV_DISABLE;
 234                 return 0;
 235         }
 236 
 237         val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
 238         if (val < 0)
 239                 return val;
 240 
 241         /* Downgrade after one link attempt */
 242         if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
 243                 *count = 1;
 244         } else {
 245                 /* Downgrade after configured retry count */
 246                 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
 247                 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
 248                 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
 249         }
 250 
 251         return 0;
 252 }
 253 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
 254 
 255 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
 256 {
 257         int val = 0, ret = 0;
 258 
 259         /* Range check the number given */
 260         if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
 261             BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
 262             count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
 263                 return -ERANGE;
 264         }
 265 
 266         val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
 267         if (val < 0)
 268                 return val;
 269 
 270         /* Se the write enable bit */
 271         val |= MII_BCM54XX_AUXCTL_MISC_WREN;
 272 
 273         if (count == DOWNSHIFT_DEV_DISABLE) {
 274                 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
 275                 return bcm54xx_auxctl_write(phydev,
 276                                             MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
 277                                             val);
 278         } else {
 279                 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
 280                 ret = bcm54xx_auxctl_write(phydev,
 281                                            MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
 282                                            val);
 283                 if (ret < 0)
 284                         return ret;
 285         }
 286 
 287         val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
 288         val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
 289                  BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
 290                  BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
 291 
 292         switch (count) {
 293         case 1:
 294                 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
 295                 break;
 296         case DOWNSHIFT_DEV_DEFAULT_COUNT:
 297                 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
 298                 break;
 299         default:
 300                 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
 301                         BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
 302                 break;
 303         }
 304 
 305         return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
 306 }
 307 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
 308 
 309 struct bcm_phy_hw_stat {
 310         const char *string;
 311         u8 reg;
 312         u8 shift;
 313         u8 bits;
 314 };
 315 
 316 /* Counters freeze at either 0xffff or 0xff, better than nothing */
 317 static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
 318         { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
 319         { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
 320         { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
 321         { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
 322         { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
 323 };
 324 
 325 int bcm_phy_get_sset_count(struct phy_device *phydev)
 326 {
 327         return ARRAY_SIZE(bcm_phy_hw_stats);
 328 }
 329 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
 330 
 331 void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
 332 {
 333         unsigned int i;
 334 
 335         for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
 336                 strlcpy(data + i * ETH_GSTRING_LEN,
 337                         bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
 338 }
 339 EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
 340 
 341 /* Caller is supposed to provide appropriate storage for the library code to
 342  * access the shadow copy
 343  */
 344 static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
 345                             unsigned int i)
 346 {
 347         struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
 348         int val;
 349         u64 ret;
 350 
 351         val = phy_read(phydev, stat.reg);
 352         if (val < 0) {
 353                 ret = U64_MAX;
 354         } else {
 355                 val >>= stat.shift;
 356                 val = val & ((1 << stat.bits) - 1);
 357                 shadow[i] += val;
 358                 ret = shadow[i];
 359         }
 360 
 361         return ret;
 362 }
 363 
 364 void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
 365                        struct ethtool_stats *stats, u64 *data)
 366 {
 367         unsigned int i;
 368 
 369         for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
 370                 data[i] = bcm_phy_get_stat(phydev, shadow, i);
 371 }
 372 EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
 373 
 374 void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
 375 {
 376         /* Reset R_CAL/RC_CAL Engine */
 377         bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
 378 
 379         /* Disable Reset R_AL/RC_CAL Engine */
 380         bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
 381 }
 382 EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
 383 
 384 int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
 385 {
 386         /* Increase VCO range to prevent unlocking problem of PLL at low
 387          * temp
 388          */
 389         bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
 390 
 391         /* Change Ki to 011 */
 392         bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
 393 
 394         /* Disable loading of TVCO buffer to bandgap, set bandgap trim
 395          * to 111
 396          */
 397         bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
 398 
 399         /* Adjust bias current trim by -3 */
 400         bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
 401 
 402         /* Switch to CORE_BASE1E */
 403         phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
 404 
 405         bcm_phy_r_rc_cal_reset(phydev);
 406 
 407         /* write AFE_RXCONFIG_0 */
 408         bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
 409 
 410         /* write AFE_RXCONFIG_1 */
 411         bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
 412 
 413         /* write AFE_RX_LP_COUNTER */
 414         bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
 415 
 416         /* write AFE_HPF_TRIM_OTHERS */
 417         bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
 418 
 419         /* write AFTE_TX_CONFIG */
 420         bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
 421 
 422         return 0;
 423 }
 424 EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
 425 
 426 MODULE_DESCRIPTION("Broadcom PHY Library");
 427 MODULE_LICENSE("GPL v2");
 428 MODULE_AUTHOR("Broadcom Corporation");

/* [<][>][^][v][top][bottom][index][help] */