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_read_rdb(struct phy_device *phydev, u16 rdb) 159 { 160 int val; 161 162 val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 163 if (val < 0) 164 return val; 165 166 return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 167 } 168 EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 169 170 int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 171 { 172 int ret; 173 174 phy_lock_mdio_bus(phydev); 175 ret = __bcm_phy_read_rdb(phydev, rdb); 176 phy_unlock_mdio_bus(phydev); 177 178 return ret; 179 } 180 EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 181 182 int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 183 { 184 int ret; 185 186 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 187 if (ret < 0) 188 return ret; 189 190 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 191 } 192 EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 193 194 int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 195 { 196 int ret; 197 198 phy_lock_mdio_bus(phydev); 199 ret = __bcm_phy_write_rdb(phydev, rdb, val); 200 phy_unlock_mdio_bus(phydev); 201 202 return ret; 203 } 204 EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 205 206 int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 207 { 208 int new, ret; 209 210 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 211 if (ret < 0) 212 return ret; 213 214 ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 215 if (ret < 0) 216 return ret; 217 218 new = (ret & ~mask) | set; 219 if (new == ret) 220 return 0; 221 222 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 223 } 224 EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 225 226 int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 227 { 228 int ret; 229 230 phy_lock_mdio_bus(phydev); 231 ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 232 phy_unlock_mdio_bus(phydev); 233 234 return ret; 235 } 236 EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 237 238 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 239 { 240 int val; 241 242 if (dll_pwr_down) { 243 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 244 if (val < 0) 245 return val; 246 247 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 248 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 249 } 250 251 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 252 if (val < 0) 253 return val; 254 255 /* Clear APD bits */ 256 val &= BCM_APD_CLR_MASK; 257 258 if (phydev->autoneg == AUTONEG_ENABLE) 259 val |= BCM54XX_SHD_APD_EN; 260 else 261 val |= BCM_NO_ANEG_APD_EN; 262 263 /* Enable energy detect single link pulse for easy wakeup */ 264 val |= BCM_APD_SINGLELP_EN; 265 266 /* Enable Auto Power-Down (APD) for the PHY */ 267 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 268 } 269 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 270 271 int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 272 { 273 int val; 274 275 /* Enable EEE at PHY level */ 276 val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 277 if (val < 0) 278 return val; 279 280 if (enable) 281 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 282 else 283 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 284 285 phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 286 287 /* Advertise EEE */ 288 val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 289 if (val < 0) 290 return val; 291 292 if (enable) 293 val |= (MDIO_EEE_100TX | MDIO_EEE_1000T); 294 else 295 val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); 296 297 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 298 299 return 0; 300 } 301 EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 302 303 int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 304 { 305 int val; 306 307 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 308 if (val < 0) 309 return val; 310 311 /* Check if wirespeed is enabled or not */ 312 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 313 *count = DOWNSHIFT_DEV_DISABLE; 314 return 0; 315 } 316 317 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 318 if (val < 0) 319 return val; 320 321 /* Downgrade after one link attempt */ 322 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 323 *count = 1; 324 } else { 325 /* Downgrade after configured retry count */ 326 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 327 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 328 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 329 } 330 331 return 0; 332 } 333 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 334 335 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 336 { 337 int val = 0, ret = 0; 338 339 /* Range check the number given */ 340 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 341 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 342 count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 343 return -ERANGE; 344 } 345 346 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 347 if (val < 0) 348 return val; 349 350 /* Se the write enable bit */ 351 val |= MII_BCM54XX_AUXCTL_MISC_WREN; 352 353 if (count == DOWNSHIFT_DEV_DISABLE) { 354 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 355 return bcm54xx_auxctl_write(phydev, 356 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 357 val); 358 } else { 359 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 360 ret = bcm54xx_auxctl_write(phydev, 361 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 362 val); 363 if (ret < 0) 364 return ret; 365 } 366 367 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 368 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 369 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 370 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 371 372 switch (count) { 373 case 1: 374 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 375 break; 376 case DOWNSHIFT_DEV_DEFAULT_COUNT: 377 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 378 break; 379 default: 380 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 381 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 382 break; 383 } 384 385 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 386 } 387 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 388 389 struct bcm_phy_hw_stat { 390 const char *string; 391 u8 reg; 392 u8 shift; 393 u8 bits; 394 }; 395 396 /* Counters freeze at either 0xffff or 0xff, better than nothing */ 397 static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 398 { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 399 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 400 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 401 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 402 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 403 }; 404 405 int bcm_phy_get_sset_count(struct phy_device *phydev) 406 { 407 return ARRAY_SIZE(bcm_phy_hw_stats); 408 } 409 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 410 411 void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 412 { 413 unsigned int i; 414 415 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 416 strlcpy(data + i * ETH_GSTRING_LEN, 417 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 418 } 419 EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 420 421 /* Caller is supposed to provide appropriate storage for the library code to 422 * access the shadow copy 423 */ 424 static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 425 unsigned int i) 426 { 427 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 428 int val; 429 u64 ret; 430 431 val = phy_read(phydev, stat.reg); 432 if (val < 0) { 433 ret = U64_MAX; 434 } else { 435 val >>= stat.shift; 436 val = val & ((1 << stat.bits) - 1); 437 shadow[i] += val; 438 ret = shadow[i]; 439 } 440 441 return ret; 442 } 443 444 void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 445 struct ethtool_stats *stats, u64 *data) 446 { 447 unsigned int i; 448 449 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 450 data[i] = bcm_phy_get_stat(phydev, shadow, i); 451 } 452 EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 453 454 void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 455 { 456 /* Reset R_CAL/RC_CAL Engine */ 457 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 458 459 /* Disable Reset R_AL/RC_CAL Engine */ 460 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 461 } 462 EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 463 464 int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 465 { 466 /* Increase VCO range to prevent unlocking problem of PLL at low 467 * temp 468 */ 469 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 470 471 /* Change Ki to 011 */ 472 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 473 474 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 475 * to 111 476 */ 477 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 478 479 /* Adjust bias current trim by -3 */ 480 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 481 482 /* Switch to CORE_BASE1E */ 483 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 484 485 bcm_phy_r_rc_cal_reset(phydev); 486 487 /* write AFE_RXCONFIG_0 */ 488 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 489 490 /* write AFE_RXCONFIG_1 */ 491 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 492 493 /* write AFE_RX_LP_COUNTER */ 494 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 495 496 /* write AFE_HPF_TRIM_OTHERS */ 497 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 498 499 /* write AFTE_TX_CONFIG */ 500 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 501 502 return 0; 503 } 504 EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 505 506 int bcm_phy_enable_jumbo(struct phy_device *phydev) 507 { 508 int ret; 509 510 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 511 if (ret < 0) 512 return ret; 513 514 /* Enable extended length packet reception */ 515 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 516 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 517 if (ret < 0) 518 return ret; 519 520 /* Enable the elastic FIFO for raising the transmission limit from 521 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 522 * latency. 523 */ 524 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 525 } 526 EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 527 528 MODULE_DESCRIPTION("Broadcom PHY Library"); 529 MODULE_LICENSE("GPL v2"); 530 MODULE_AUTHOR("Broadcom Corporation"); 531