1 /* 2 * Broadcom BCM7xxx internal transceivers support. 3 * 4 * Copyright (C) 2014, Broadcom Corporation 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 /* 40nm 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 31 /* 28nm only register definitions */ 32 #define MISC_ADDR(base, channel) base, channel 33 34 #define DSP_TAP10 MISC_ADDR(0x0a, 0) 35 #define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) 36 #define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) 37 #define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) 38 39 #define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) 40 #define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) 41 #define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) 42 #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) 43 #define AFE_TX_CONFIG MISC_ADDR(0x39, 0) 44 #define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) 45 #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) 46 #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) 47 48 struct bcm7xxx_phy_priv { 49 u64 *stats; 50 }; 51 52 static void r_rc_cal_reset(struct phy_device *phydev) 53 { 54 /* Reset R_CAL/RC_CAL Engine */ 55 bcm_phy_write_exp(phydev, 0x00b0, 0x0010); 56 57 /* Disable Reset R_AL/RC_CAL Engine */ 58 bcm_phy_write_exp(phydev, 0x00b0, 0x0000); 59 } 60 61 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) 62 { 63 /* Increase VCO range to prevent unlocking problem of PLL at low 64 * temp 65 */ 66 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 67 68 /* Change Ki to 011 */ 69 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 70 71 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 72 * to 111 73 */ 74 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 75 76 /* Adjust bias current trim by -3 */ 77 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 78 79 /* Switch to CORE_BASE1E */ 80 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 81 82 r_rc_cal_reset(phydev); 83 84 /* write AFE_RXCONFIG_0 */ 85 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 86 87 /* write AFE_RXCONFIG_1 */ 88 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 89 90 /* write AFE_RX_LP_COUNTER */ 91 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 92 93 /* write AFE_HPF_TRIM_OTHERS */ 94 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 95 96 /* write AFTE_TX_CONFIG */ 97 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 98 99 return 0; 100 } 101 102 static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) 103 { 104 /* AFE_RXCONFIG_0 */ 105 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); 106 107 /* AFE_RXCONFIG_1 */ 108 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 109 110 /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ 111 bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); 112 113 /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ 114 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 115 116 /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ 117 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); 118 119 /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 120 bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 121 122 /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ 123 bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); 124 125 /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 126 * offset for HT=0 code 127 */ 128 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 129 130 /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 131 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); 132 133 /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 134 bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); 135 136 /* Reset R_CAL/RC_CAL engine */ 137 r_rc_cal_reset(phydev); 138 139 return 0; 140 } 141 142 static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) 143 { 144 /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ 145 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 146 147 /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ 148 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); 149 150 /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 151 bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 152 153 /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 154 * offset for HT=0 code 155 */ 156 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 157 158 /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 159 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); 160 161 /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 162 bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); 163 164 /* Reset R_CAL/RC_CAL engine */ 165 r_rc_cal_reset(phydev); 166 167 return 0; 168 } 169 170 static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev) 171 { 172 /* +1 RC_CAL codes for RL centering for both LT and HT conditions */ 173 bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003); 174 175 /* Cut master bias current by 2% to compensate for RC_CAL offset */ 176 bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b); 177 178 /* Improve hybrid leakage */ 179 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3); 180 181 /* Change rx_on_tune 8 to 0xf */ 182 bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6); 183 184 /* Change 100Tx EEE bandwidth */ 185 bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d); 186 187 /* Enable ffe zero detection for Vitesse interoperability */ 188 bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015); 189 190 r_rc_cal_reset(phydev); 191 192 return 0; 193 } 194 195 static int bcm7xxx_28nm_config_init(struct phy_device *phydev) 196 { 197 u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); 198 u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); 199 u8 count; 200 int ret = 0; 201 202 /* Newer devices have moved the revision information back into a 203 * standard location in MII_PHYS_ID[23] 204 */ 205 if (rev == 0) 206 rev = phydev->phy_id & ~phydev->drv->phy_id_mask; 207 208 pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", 209 phydev_name(phydev), phydev->drv->name, rev, patch); 210 211 /* Dummy read to a register to workaround an issue upon reset where the 212 * internal inverter may not allow the first MDIO transaction to pass 213 * the MDIO management controller and make us return 0xffff for such 214 * reads. 215 */ 216 phy_read(phydev, MII_BMSR); 217 218 switch (rev) { 219 case 0xb0: 220 ret = bcm7xxx_28nm_b0_afe_config_init(phydev); 221 break; 222 case 0xd0: 223 ret = bcm7xxx_28nm_d0_afe_config_init(phydev); 224 break; 225 case 0xe0: 226 case 0xf0: 227 /* Rev G0 introduces a roll over */ 228 case 0x10: 229 ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev); 230 break; 231 case 0x01: 232 ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev); 233 break; 234 default: 235 break; 236 } 237 238 if (ret) 239 return ret; 240 241 ret = bcm_phy_downshift_get(phydev, &count); 242 if (ret) 243 return ret; 244 245 /* Only enable EEE if Wirespeed/downshift is disabled */ 246 ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); 247 if (ret) 248 return ret; 249 250 return bcm_phy_enable_apd(phydev, true); 251 } 252 253 static int bcm7xxx_28nm_resume(struct phy_device *phydev) 254 { 255 int ret; 256 257 /* Re-apply workarounds coming out suspend/resume */ 258 ret = bcm7xxx_28nm_config_init(phydev); 259 if (ret) 260 return ret; 261 262 /* 28nm Gigabit PHYs come out of reset without any half-duplex 263 * or "hub" compliant advertised mode, fix that. This does not 264 * cause any problems with the PHY library since genphy_config_aneg() 265 * gracefully handles auto-negotiated and forced modes. 266 */ 267 return genphy_config_aneg(phydev); 268 } 269 270 static int phy_set_clr_bits(struct phy_device *dev, int location, 271 int set_mask, int clr_mask) 272 { 273 int v, ret; 274 275 v = phy_read(dev, location); 276 if (v < 0) 277 return v; 278 279 v &= ~clr_mask; 280 v |= set_mask; 281 282 ret = phy_write(dev, location, v); 283 if (ret < 0) 284 return ret; 285 286 return v; 287 } 288 289 static int bcm7xxx_config_init(struct phy_device *phydev) 290 { 291 int ret; 292 293 /* Enable 64 clock MDIO */ 294 phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO); 295 phy_read(phydev, MII_BCM7XXX_AUX_MODE); 296 297 /* set shadow mode 2 */ 298 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 299 MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); 300 if (ret < 0) 301 return ret; 302 303 /* set iddq_clkbias */ 304 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); 305 udelay(10); 306 307 /* reset iddq_clkbias */ 308 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); 309 310 phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); 311 312 /* reset shadow mode 2 */ 313 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2); 314 if (ret < 0) 315 return ret; 316 317 return 0; 318 } 319 320 /* Workaround for putting the PHY in IDDQ mode, required 321 * for all BCM7XXX 40nm and 65nm PHYs 322 */ 323 static int bcm7xxx_suspend(struct phy_device *phydev) 324 { 325 int ret; 326 const struct bcm7xxx_regs { 327 int reg; 328 u16 value; 329 } bcm7xxx_suspend_cfg[] = { 330 { MII_BCM7XXX_TEST, 0x008b }, 331 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, 332 { MII_BCM7XXX_100TX_DISC, 0x7000 }, 333 { MII_BCM7XXX_TEST, 0x000f }, 334 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, 335 { MII_BCM7XXX_TEST, 0x000b }, 336 }; 337 unsigned int i; 338 339 for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { 340 ret = phy_write(phydev, 341 bcm7xxx_suspend_cfg[i].reg, 342 bcm7xxx_suspend_cfg[i].value); 343 if (ret) 344 return ret; 345 } 346 347 return 0; 348 } 349 350 static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev, 351 struct ethtool_tunable *tuna, 352 void *data) 353 { 354 switch (tuna->id) { 355 case ETHTOOL_PHY_DOWNSHIFT: 356 return bcm_phy_downshift_get(phydev, (u8 *)data); 357 default: 358 return -EOPNOTSUPP; 359 } 360 } 361 362 static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, 363 struct ethtool_tunable *tuna, 364 const void *data) 365 { 366 u8 count = *(u8 *)data; 367 int ret; 368 369 switch (tuna->id) { 370 case ETHTOOL_PHY_DOWNSHIFT: 371 ret = bcm_phy_downshift_set(phydev, count); 372 break; 373 default: 374 return -EOPNOTSUPP; 375 } 376 377 if (ret) 378 return ret; 379 380 /* Disable EEE advertisment since this prevents the PHY 381 * from successfully linking up, trigger auto-negotiation restart 382 * to let the MAC decide what to do. 383 */ 384 ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); 385 if (ret) 386 return ret; 387 388 return genphy_restart_aneg(phydev); 389 } 390 391 static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, 392 struct ethtool_stats *stats, u64 *data) 393 { 394 struct bcm7xxx_phy_priv *priv = phydev->priv; 395 396 bcm_phy_get_stats(phydev, priv->stats, stats, data); 397 } 398 399 static int bcm7xxx_28nm_probe(struct phy_device *phydev) 400 { 401 struct bcm7xxx_phy_priv *priv; 402 403 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 404 if (!priv) 405 return -ENOMEM; 406 407 phydev->priv = priv; 408 409 priv->stats = devm_kcalloc(&phydev->mdio.dev, 410 bcm_phy_get_sset_count(phydev), sizeof(u64), 411 GFP_KERNEL); 412 if (!priv->stats) 413 return -ENOMEM; 414 415 return 0; 416 } 417 418 #define BCM7XXX_28NM_GPHY(_oui, _name) \ 419 { \ 420 .phy_id = (_oui), \ 421 .phy_id_mask = 0xfffffff0, \ 422 .name = _name, \ 423 .features = PHY_GBIT_FEATURES, \ 424 .flags = PHY_IS_INTERNAL, \ 425 .config_init = bcm7xxx_28nm_config_init, \ 426 .config_aneg = genphy_config_aneg, \ 427 .read_status = genphy_read_status, \ 428 .resume = bcm7xxx_28nm_resume, \ 429 .get_tunable = bcm7xxx_28nm_get_tunable, \ 430 .set_tunable = bcm7xxx_28nm_set_tunable, \ 431 .get_sset_count = bcm_phy_get_sset_count, \ 432 .get_strings = bcm_phy_get_strings, \ 433 .get_stats = bcm7xxx_28nm_get_phy_stats, \ 434 .probe = bcm7xxx_28nm_probe, \ 435 } 436 437 #define BCM7XXX_40NM_EPHY(_oui, _name) \ 438 { \ 439 .phy_id = (_oui), \ 440 .phy_id_mask = 0xfffffff0, \ 441 .name = _name, \ 442 .features = PHY_BASIC_FEATURES, \ 443 .flags = PHY_IS_INTERNAL, \ 444 .config_init = bcm7xxx_config_init, \ 445 .config_aneg = genphy_config_aneg, \ 446 .read_status = genphy_read_status, \ 447 .suspend = bcm7xxx_suspend, \ 448 .resume = bcm7xxx_config_init, \ 449 } 450 451 static struct phy_driver bcm7xxx_driver[] = { 452 BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), 453 BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"), 454 BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), 455 BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), 456 BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"), 457 BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), 458 BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), 459 BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), 460 BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"), 461 BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"), 462 BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"), 463 BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"), 464 BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"), 465 }; 466 467 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { 468 { PHY_ID_BCM7250, 0xfffffff0, }, 469 { PHY_ID_BCM7278, 0xfffffff0, }, 470 { PHY_ID_BCM7364, 0xfffffff0, }, 471 { PHY_ID_BCM7366, 0xfffffff0, }, 472 { PHY_ID_BCM7346, 0xfffffff0, }, 473 { PHY_ID_BCM7362, 0xfffffff0, }, 474 { PHY_ID_BCM7425, 0xfffffff0, }, 475 { PHY_ID_BCM7429, 0xfffffff0, }, 476 { PHY_ID_BCM74371, 0xfffffff0, }, 477 { PHY_ID_BCM7439, 0xfffffff0, }, 478 { PHY_ID_BCM7435, 0xfffffff0, }, 479 { PHY_ID_BCM7445, 0xfffffff0, }, 480 { } 481 }; 482 483 module_phy_driver(bcm7xxx_driver); 484 485 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); 486 487 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); 488 MODULE_LICENSE("GPL"); 489 MODULE_AUTHOR("Broadcom Corporation"); 490