1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Broadcom PHY drivers 4 * 5 * Copyright 2010-2011 Freescale Semiconductor, Inc. 6 * author Andy Fleming 7 */ 8 #include <common.h> 9 #include <phy.h> 10 11 /* Broadcom BCM54xx -- taken from linux sungem_phy */ 12 #define MIIM_BCM54xx_AUXCNTL 0x18 13 #define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7)) 14 #define MIIM_BCM54xx_AUXSTATUS 0x19 15 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700 16 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8 17 18 #define MIIM_BCM54XX_SHD 0x1c 19 #define MIIM_BCM54XX_SHD_WRITE 0x8000 20 #define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10) 21 #define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0) 22 #define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \ 23 (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \ 24 MIIM_BCM54XX_SHD_DATA(data)) 25 26 #define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */ 27 #define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */ 28 #define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */ 29 #define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */ 30 31 #define MIIM_BCM_AUXCNTL_SHDWSEL_MISC 0x0007 32 #define MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN 0x0800 33 34 #define MIIM_BCM_CHANNEL_WIDTH 0x2000 35 36 static void bcm_phy_write_misc(struct phy_device *phydev, 37 u16 reg, u16 chl, u16 value) 38 { 39 int reg_val; 40 41 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 42 MIIM_BCM_AUXCNTL_SHDWSEL_MISC); 43 44 reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL); 45 reg_val |= MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN; 46 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg_val); 47 48 reg_val = (chl * MIIM_BCM_CHANNEL_WIDTH) | reg; 49 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, reg_val); 50 51 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, value); 52 } 53 54 /* Broadcom BCM5461S */ 55 static int bcm5461_config(struct phy_device *phydev) 56 { 57 unsigned int mii_reg = 0; 58 genphy_config_aneg(phydev); 59 60 phy_reset(phydev); 61 62 if(phydev->drv->uid == 0x03625d12) { 63 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x0) & ~BIT(10); 64 phy_write(phydev, MDIO_DEVAD_NONE, 0x0, mii_reg); 65 66 //disable skew 67 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 0x7007); 68 mii_reg = (phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL) & 0x0af0) | 0xf007; 69 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, mii_reg); 70 71 //disable delay 72 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, 0xc00); 73 mii_reg = 0x8c00; 74 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, mii_reg); 75 } 76 77 return 0; 78 } 79 80 static int bcm54xx_parse_status(struct phy_device *phydev) 81 { 82 unsigned int mii_reg; 83 84 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS); 85 86 switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> 87 MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) { 88 case 1: 89 phydev->duplex = DUPLEX_HALF; 90 phydev->speed = SPEED_10; 91 break; 92 case 2: 93 phydev->duplex = DUPLEX_FULL; 94 phydev->speed = SPEED_10; 95 break; 96 case 3: 97 phydev->duplex = DUPLEX_HALF; 98 phydev->speed = SPEED_100; 99 break; 100 case 5: 101 phydev->duplex = DUPLEX_FULL; 102 phydev->speed = SPEED_100; 103 break; 104 case 6: 105 phydev->duplex = DUPLEX_HALF; 106 phydev->speed = SPEED_1000; 107 break; 108 case 7: 109 phydev->duplex = DUPLEX_FULL; 110 phydev->speed = SPEED_1000; 111 break; 112 default: 113 printf("Auto-neg error, defaulting to 10BT/HD\n"); 114 phydev->duplex = DUPLEX_HALF; 115 phydev->speed = SPEED_10; 116 break; 117 } 118 119 return 0; 120 } 121 122 static int bcm54xx_startup(struct phy_device *phydev) 123 { 124 int ret; 125 126 /* Read the Status (2x to make sure link is right) */ 127 ret = genphy_update_link(phydev); 128 if (ret) 129 return ret; 130 131 return bcm54xx_parse_status(phydev); 132 } 133 134 /* Broadcom BCM5482S */ 135 /* 136 * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain 137 * circumstances. eg a gigabit TSEC connected to a gigabit switch with 138 * a 4-wire ethernet cable. Both ends advertise gigabit, but can't 139 * link. "Ethernet@Wirespeed" reduces advertised speed until link 140 * can be achieved. 141 */ 142 static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg) 143 { 144 return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010; 145 } 146 147 static int bcm5482_config(struct phy_device *phydev) 148 { 149 unsigned int reg; 150 151 /* reset the PHY */ 152 reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); 153 reg |= BMCR_RESET; 154 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg); 155 156 /* Setup read from auxilary control shadow register 7 */ 157 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 158 MIIM_BCM54xx_AUXCNTL_ENCODE(7)); 159 /* Read Misc Control register and or in Ethernet@Wirespeed */ 160 reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL); 161 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg); 162 163 /* Initial config/enable of secondary SerDes interface */ 164 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, 165 MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf)); 166 /* Write intial value to secondary SerDes Contol */ 167 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, 168 MIIM_BCM54XX_EXP_SEL_SSD | 0); 169 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, 170 BMCR_ANRESTART); 171 /* Enable copper/fiber auto-detect */ 172 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, 173 MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201)); 174 175 genphy_config_aneg(phydev); 176 177 return 0; 178 } 179 180 static int bcm_cygnus_startup(struct phy_device *phydev) 181 { 182 int ret; 183 184 /* Read the Status (2x to make sure link is right) */ 185 ret = genphy_update_link(phydev); 186 if (ret) 187 return ret; 188 189 return genphy_parse_link(phydev); 190 } 191 192 static void bcm_cygnus_afe(struct phy_device *phydev) 193 { 194 /* ensures smdspclk is enabled */ 195 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 0x0c30); 196 197 /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ 198 bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); 199 200 /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode for all modes*/ 201 bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); 202 203 /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ 204 bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); 205 206 /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ 207 bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); 208 209 /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ 210 bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); 211 212 /* Adjust bias current trim to overcome digital offSet */ 213 phy_write(phydev, MDIO_DEVAD_NONE, 0x1E, 0x02); 214 215 /* make rcal=100, since rdb default is 000 */ 216 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B1); 217 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010); 218 219 /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ 220 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0); 221 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010); 222 223 /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ 224 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0); 225 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0000); 226 } 227 228 static int bcm_cygnus_config(struct phy_device *phydev) 229 { 230 genphy_config_aneg(phydev); 231 phy_reset(phydev); 232 /* AFE settings for PHY stability */ 233 bcm_cygnus_afe(phydev); 234 /* Forcing aneg after applying the AFE settings */ 235 genphy_restart_aneg(phydev); 236 237 return 0; 238 } 239 240 /* 241 * Find out if PHY is in copper or serdes mode by looking at Expansion Reg 242 * 0x42 - "Operating Mode Status Register" 243 */ 244 static int bcm5482_is_serdes(struct phy_device *phydev) 245 { 246 u16 val; 247 int serdes = 0; 248 249 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, 250 MIIM_BCM54XX_EXP_SEL_ER | 0x42); 251 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA); 252 253 switch (val & 0x1f) { 254 case 0x0d: /* RGMII-to-100Base-FX */ 255 case 0x0e: /* RGMII-to-SGMII */ 256 case 0x0f: /* RGMII-to-SerDes */ 257 case 0x12: /* SGMII-to-SerDes */ 258 case 0x13: /* SGMII-to-100Base-FX */ 259 case 0x16: /* SerDes-to-Serdes */ 260 serdes = 1; 261 break; 262 case 0x6: /* RGMII-to-Copper */ 263 case 0x14: /* SGMII-to-Copper */ 264 case 0x17: /* SerDes-to-Copper */ 265 break; 266 default: 267 printf("ERROR, invalid PHY mode (0x%x\n)", val); 268 break; 269 } 270 271 return serdes; 272 } 273 274 /* 275 * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating 276 * Mode Status Register" 277 */ 278 static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev) 279 { 280 u16 val; 281 int i = 0; 282 283 /* Wait 1s for link - Clause 37 autonegotiation happens very fast */ 284 while (1) { 285 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, 286 MIIM_BCM54XX_EXP_SEL_ER | 0x42); 287 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA); 288 289 if (val & 0x8000) 290 break; 291 292 if (i++ > 1000) { 293 phydev->link = 0; 294 return 1; 295 } 296 297 udelay(1000); /* 1 ms */ 298 } 299 300 phydev->link = 1; 301 switch ((val >> 13) & 0x3) { 302 case (0x00): 303 phydev->speed = 10; 304 break; 305 case (0x01): 306 phydev->speed = 100; 307 break; 308 case (0x02): 309 phydev->speed = 1000; 310 break; 311 } 312 313 phydev->duplex = (val & 0x1000) == 0x1000; 314 315 return 0; 316 } 317 318 /* 319 * Figure out if BCM5482 is in serdes or copper mode and determine link 320 * configuration accordingly 321 */ 322 static int bcm5482_startup(struct phy_device *phydev) 323 { 324 int ret; 325 326 if (bcm5482_is_serdes(phydev)) { 327 bcm5482_parse_serdes_sr(phydev); 328 phydev->port = PORT_FIBRE; 329 return 0; 330 } 331 332 /* Wait for auto-negotiation to complete or fail */ 333 ret = genphy_update_link(phydev); 334 if (ret) 335 return ret; 336 337 /* Parse BCM54xx copper aux status register */ 338 return bcm54xx_parse_status(phydev); 339 } 340 341 static struct phy_driver BCM54616S_driver = { 342 .name = "Broadcom BCM54616S", 343 .uid = 0x03625d12, 344 .mask = 0xffffffff, 345 .features = PHY_GBIT_FEATURES, 346 .config = &bcm5461_config, 347 .startup = &bcm54xx_startup, 348 .shutdown = &genphy_shutdown, 349 }; 350 351 static struct phy_driver BCM5461S_driver = { 352 .name = "Broadcom BCM5461S", 353 .uid = 0x2060c0, 354 .mask = 0xfffff0, 355 .features = PHY_GBIT_FEATURES, 356 .config = &bcm5461_config, 357 .startup = &bcm54xx_startup, 358 .shutdown = &genphy_shutdown, 359 }; 360 361 static struct phy_driver BCM5464S_driver = { 362 .name = "Broadcom BCM5464S", 363 .uid = 0x2060b0, 364 .mask = 0xfffff0, 365 .features = PHY_GBIT_FEATURES, 366 .config = &bcm5461_config, 367 .startup = &bcm54xx_startup, 368 .shutdown = &genphy_shutdown, 369 }; 370 371 static struct phy_driver BCM5482S_driver = { 372 .name = "Broadcom BCM5482S", 373 .uid = 0x143bcb0, 374 .mask = 0xffffff0, 375 .features = PHY_GBIT_FEATURES, 376 .config = &bcm5482_config, 377 .startup = &bcm5482_startup, 378 .shutdown = &genphy_shutdown, 379 }; 380 381 static struct phy_driver BCM_CYGNUS_driver = { 382 .name = "Broadcom CYGNUS GPHY", 383 .uid = 0xae025200, 384 .mask = 0xfffff0, 385 .features = PHY_GBIT_FEATURES, 386 .config = &bcm_cygnus_config, 387 .startup = &bcm_cygnus_startup, 388 .shutdown = &genphy_shutdown, 389 }; 390 391 int phy_broadcom_init(void) 392 { 393 phy_register(&BCM54616S_driver); 394 phy_register(&BCM5482S_driver); 395 phy_register(&BCM5464S_driver); 396 phy_register(&BCM5461S_driver); 397 phy_register(&BCM_CYGNUS_driver); 398 399 return 0; 400 } 401