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 <linux/bitops.h> 16 #include <linux/brcmphy.h> 17 #include <linux/mdio.h> 18 19 /* Broadcom BCM7xxx internal PHY registers */ 20 #define MII_BCM7XXX_CHANNEL_WIDTH 0x2000 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_BCM7XX_64CLK_MDIO BIT(12) 28 #define MII_BCM7XXX_CORE_BASE1E 0x1e 29 #define MII_BCM7XXX_TEST 0x1f 30 #define MII_BCM7XXX_SHD_MODE_2 BIT(2) 31 32 /* 28nm only register definitions */ 33 #define MISC_ADDR(base, channel) base, channel 34 35 #define DSP_TAP10 MISC_ADDR(0x0a, 0) 36 #define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) 37 #define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) 38 #define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) 39 40 #define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) 41 #define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) 42 #define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) 43 #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) 44 #define AFE_TX_CONFIG MISC_ADDR(0x39, 0) 45 #define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) 46 #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) 47 #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) 48 49 #define CORE_EXPB0 0xb0 50 51 static void phy_write_exp(struct phy_device *phydev, 52 u16 reg, u16 value) 53 { 54 phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); 55 phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 56 } 57 58 static void phy_write_misc(struct phy_device *phydev, 59 u16 reg, u16 chl, u16 value) 60 { 61 int tmp; 62 63 phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 64 65 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 66 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 67 phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 68 69 tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; 70 phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); 71 72 phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 73 } 74 75 static void r_rc_cal_reset(struct phy_device *phydev) 76 { 77 /* Reset R_CAL/RC_CAL Engine */ 78 phy_write_exp(phydev, 0x00b0, 0x0010); 79 80 /* Disable Reset R_AL/RC_CAL Engine */ 81 phy_write_exp(phydev, 0x00b0, 0x0000); 82 } 83 84 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) 85 { 86 /* Increase VCO range to prevent unlocking problem of PLL at low 87 * temp 88 */ 89 phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 90 91 /* Change Ki to 011 */ 92 phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 93 94 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 95 * to 111 96 */ 97 phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 98 99 /* Adjust bias current trim by -3 */ 100 phy_write_misc(phydev, DSP_TAP10, 0x690b); 101 102 /* Switch to CORE_BASE1E */ 103 phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); 104 105 r_rc_cal_reset(phydev); 106 107 /* write AFE_RXCONFIG_0 */ 108 phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 109 110 /* write AFE_RXCONFIG_1 */ 111 phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 112 113 /* write AFE_RX_LP_COUNTER */ 114 phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 115 116 /* write AFE_HPF_TRIM_OTHERS */ 117 phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 118 119 /* write AFTE_TX_CONFIG */ 120 phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 121 122 return 0; 123 } 124 125 static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) 126 { 127 /* AFE_RXCONFIG_0 */ 128 phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); 129 130 /* AFE_RXCONFIG_1 */ 131 phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 132 133 /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ 134 phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); 135 136 /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ 137 phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 138 139 /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ 140 phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); 141 142 /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 143 phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 144 145 /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ 146 phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); 147 148 /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 149 * offset for HT=0 code 150 */ 151 phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 152 153 /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 154 phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); 155 156 /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 157 phy_write_misc(phydev, DSP_TAP10, 0x011b); 158 159 /* Reset R_CAL/RC_CAL engine */ 160 r_rc_cal_reset(phydev); 161 162 return 0; 163 } 164 165 static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) 166 { 167 /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ 168 phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 169 170 /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ 171 phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); 172 173 /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 174 phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 175 176 /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 177 * offset for HT=0 code 178 */ 179 phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 180 181 /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 182 phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); 183 184 /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 185 phy_write_misc(phydev, DSP_TAP10, 0x011b); 186 187 /* Reset R_CAL/RC_CAL engine */ 188 r_rc_cal_reset(phydev); 189 190 return 0; 191 } 192 193 static int bcm7xxx_apd_enable(struct phy_device *phydev) 194 { 195 int val; 196 197 /* Enable powering down of the DLL during auto-power down */ 198 val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); 199 if (val < 0) 200 return val; 201 202 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 203 bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); 204 205 /* Enable auto-power down */ 206 val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); 207 if (val < 0) 208 return val; 209 210 val |= BCM54XX_SHD_APD_EN; 211 return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); 212 } 213 214 static int bcm7xxx_eee_enable(struct phy_device *phydev) 215 { 216 int val; 217 218 val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, 219 MDIO_MMD_AN, phydev->addr); 220 if (val < 0) 221 return val; 222 223 /* Enable general EEE feature at the PHY level */ 224 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 225 226 phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, 227 MDIO_MMD_AN, phydev->addr, val); 228 229 /* Advertise supported modes */ 230 val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, 231 MDIO_MMD_AN, phydev->addr); 232 233 val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); 234 phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, 235 MDIO_MMD_AN, phydev->addr, val); 236 237 return 0; 238 } 239 240 static int bcm7xxx_28nm_config_init(struct phy_device *phydev) 241 { 242 u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); 243 u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); 244 int ret = 0; 245 246 pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", 247 dev_name(&phydev->dev), phydev->drv->name, rev, patch); 248 249 /* Dummy read to a register to workaround an issue upon reset where the 250 * internal inverter may not allow the first MDIO transaction to pass 251 * the MDIO management controller and make us return 0xffff for such 252 * reads. 253 */ 254 phy_read(phydev, MII_BMSR); 255 256 switch (rev) { 257 case 0xb0: 258 ret = bcm7xxx_28nm_b0_afe_config_init(phydev); 259 break; 260 case 0xd0: 261 ret = bcm7xxx_28nm_d0_afe_config_init(phydev); 262 break; 263 case 0xe0: 264 case 0xf0: 265 /* Rev G0 introduces a roll over */ 266 case 0x10: 267 ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev); 268 break; 269 default: 270 break; 271 } 272 273 if (ret) 274 return ret; 275 276 ret = bcm7xxx_eee_enable(phydev); 277 if (ret) 278 return ret; 279 280 return bcm7xxx_apd_enable(phydev); 281 } 282 283 static int bcm7xxx_28nm_resume(struct phy_device *phydev) 284 { 285 int ret; 286 287 /* Re-apply workarounds coming out suspend/resume */ 288 ret = bcm7xxx_28nm_config_init(phydev); 289 if (ret) 290 return ret; 291 292 /* 28nm Gigabit PHYs come out of reset without any half-duplex 293 * or "hub" compliant advertised mode, fix that. This does not 294 * cause any problems with the PHY library since genphy_config_aneg() 295 * gracefully handles auto-negotiated and forced modes. 296 */ 297 return genphy_config_aneg(phydev); 298 } 299 300 static int phy_set_clr_bits(struct phy_device *dev, int location, 301 int set_mask, int clr_mask) 302 { 303 int v, ret; 304 305 v = phy_read(dev, location); 306 if (v < 0) 307 return v; 308 309 v &= ~clr_mask; 310 v |= set_mask; 311 312 ret = phy_write(dev, location, v); 313 if (ret < 0) 314 return ret; 315 316 return v; 317 } 318 319 static int bcm7xxx_config_init(struct phy_device *phydev) 320 { 321 int ret; 322 323 /* Enable 64 clock MDIO */ 324 phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO); 325 phy_read(phydev, MII_BCM7XXX_AUX_MODE); 326 327 /* Workaround only required for 100Mbits/sec capable PHYs */ 328 if (phydev->supported & PHY_GBIT_FEATURES) 329 return 0; 330 331 /* set shadow mode 2 */ 332 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 333 MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); 334 if (ret < 0) 335 return ret; 336 337 /* set iddq_clkbias */ 338 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); 339 udelay(10); 340 341 /* reset iddq_clkbias */ 342 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); 343 344 phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); 345 346 /* reset shadow mode 2 */ 347 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0); 348 if (ret < 0) 349 return ret; 350 351 return 0; 352 } 353 354 /* Workaround for putting the PHY in IDDQ mode, required 355 * for all BCM7XXX 40nm and 65nm PHYs 356 */ 357 static int bcm7xxx_suspend(struct phy_device *phydev) 358 { 359 int ret; 360 const struct bcm7xxx_regs { 361 int reg; 362 u16 value; 363 } bcm7xxx_suspend_cfg[] = { 364 { MII_BCM7XXX_TEST, 0x008b }, 365 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, 366 { MII_BCM7XXX_100TX_DISC, 0x7000 }, 367 { MII_BCM7XXX_TEST, 0x000f }, 368 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, 369 { MII_BCM7XXX_TEST, 0x000b }, 370 }; 371 unsigned int i; 372 373 for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { 374 ret = phy_write(phydev, 375 bcm7xxx_suspend_cfg[i].reg, 376 bcm7xxx_suspend_cfg[i].value); 377 if (ret) 378 return ret; 379 } 380 381 return 0; 382 } 383 384 static int bcm7xxx_dummy_config_init(struct phy_device *phydev) 385 { 386 return 0; 387 } 388 389 #define BCM7XXX_28NM_GPHY(_oui, _name) \ 390 { \ 391 .phy_id = (_oui), \ 392 .phy_id_mask = 0xfffffff0, \ 393 .name = _name, \ 394 .features = PHY_GBIT_FEATURES | \ 395 SUPPORTED_Pause | SUPPORTED_Asym_Pause, \ 396 .flags = PHY_IS_INTERNAL, \ 397 .config_init = bcm7xxx_28nm_config_init, \ 398 .config_aneg = genphy_config_aneg, \ 399 .read_status = genphy_read_status, \ 400 .resume = bcm7xxx_28nm_resume, \ 401 .driver = { .owner = THIS_MODULE }, \ 402 } 403 404 static struct phy_driver bcm7xxx_driver[] = { 405 BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), 406 BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), 407 BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), 408 BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), 409 BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), 410 BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), 411 { 412 .phy_id = PHY_ID_BCM7425, 413 .phy_id_mask = 0xfffffff0, 414 .name = "Broadcom BCM7425", 415 .features = PHY_GBIT_FEATURES | 416 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 417 .flags = PHY_IS_INTERNAL, 418 .config_init = bcm7xxx_config_init, 419 .config_aneg = genphy_config_aneg, 420 .read_status = genphy_read_status, 421 .suspend = bcm7xxx_suspend, 422 .resume = bcm7xxx_config_init, 423 .driver = { .owner = THIS_MODULE }, 424 }, { 425 .phy_id = PHY_ID_BCM7429, 426 .phy_id_mask = 0xfffffff0, 427 .name = "Broadcom BCM7429", 428 .features = PHY_GBIT_FEATURES | 429 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 430 .flags = PHY_IS_INTERNAL, 431 .config_init = bcm7xxx_config_init, 432 .config_aneg = genphy_config_aneg, 433 .read_status = genphy_read_status, 434 .suspend = bcm7xxx_suspend, 435 .resume = bcm7xxx_config_init, 436 .driver = { .owner = THIS_MODULE }, 437 }, { 438 .phy_id = PHY_BCM_OUI_4, 439 .phy_id_mask = 0xffff0000, 440 .name = "Broadcom BCM7XXX 40nm", 441 .features = PHY_GBIT_FEATURES | 442 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 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 .driver = { .owner = THIS_MODULE }, 450 }, { 451 .phy_id = PHY_BCM_OUI_5, 452 .phy_id_mask = 0xffffff00, 453 .name = "Broadcom BCM7XXX 65nm", 454 .features = PHY_BASIC_FEATURES | 455 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 456 .flags = PHY_IS_INTERNAL, 457 .config_init = bcm7xxx_dummy_config_init, 458 .config_aneg = genphy_config_aneg, 459 .read_status = genphy_read_status, 460 .suspend = bcm7xxx_suspend, 461 .resume = bcm7xxx_config_init, 462 .driver = { .owner = THIS_MODULE }, 463 } }; 464 465 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { 466 { PHY_ID_BCM7250, 0xfffffff0, }, 467 { PHY_ID_BCM7364, 0xfffffff0, }, 468 { PHY_ID_BCM7366, 0xfffffff0, }, 469 { PHY_ID_BCM7425, 0xfffffff0, }, 470 { PHY_ID_BCM7429, 0xfffffff0, }, 471 { PHY_ID_BCM7439, 0xfffffff0, }, 472 { PHY_ID_BCM7445, 0xfffffff0, }, 473 { PHY_BCM_OUI_4, 0xffff0000 }, 474 { PHY_BCM_OUI_5, 0xffffff00 }, 475 { } 476 }; 477 478 module_phy_driver(bcm7xxx_driver); 479 480 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); 481 482 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); 483 MODULE_LICENSE("GPL"); 484 MODULE_AUTHOR("Broadcom Corporation"); 485