Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.6-rc6 428 lines 10 kB view raw
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 17int 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} 27EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 28 29int 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} 44EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 45 46int 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} 55EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 56 57int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 58{ 59 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 60} 61EXPORT_SYMBOL(bcm54xx_auxctl_write); 62 63int 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} 85EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 86 87int 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} 109EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 110 111int 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} 122EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 123 124int 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} 139EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 140 141int 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} 146EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 147 148int 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} 156EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 157 158int 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} 189EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 190 191int 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} 221EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 222 223int 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} 253EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 254 255int 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} 307EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 308 309struct 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 */ 317static 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 325int bcm_phy_get_sset_count(struct phy_device *phydev) 326{ 327 return ARRAY_SIZE(bcm_phy_hw_stats); 328} 329EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 330 331void 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} 339EXPORT_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 */ 344static 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 364void 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} 372EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 373 374void 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} 382EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 383 384int 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} 424EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 425 426MODULE_DESCRIPTION("Broadcom PHY Library"); 427MODULE_LICENSE("GPL v2"); 428MODULE_AUTHOR("Broadcom Corporation");