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 18 /* Broadcom BCM7xxx internal PHY registers */ 19 #define MII_BCM7XXX_CHANNEL_WIDTH 0x2000 20 21 /* 40nm only register definitions */ 22 #define MII_BCM7XXX_100TX_AUX_CTL 0x10 23 #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 24 #define MII_BCM7XXX_100TX_DISC 0x14 25 #define MII_BCM7XXX_AUX_MODE 0x1d 26 #define MII_BCM7XX_64CLK_MDIO BIT(12) 27 #define MII_BCM7XXX_CORE_BASE1E 0x1e 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_RX_LP_COUNTER MISC_ADDR(0x38, 3) 42 #define AFE_TX_CONFIG MISC_ADDR(0x39, 0) 43 #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) 44 45 #define CORE_EXPB0 0xb0 46 47 static int bcm7445_config_init(struct phy_device *phydev) 48 { 49 int ret; 50 const struct bcm7445_regs { 51 int reg; 52 u16 value; 53 } bcm7445_regs_cfg[] = { 54 /* increases ADC latency by 24ns */ 55 { MII_BCM54XX_EXP_SEL, 0x0038 }, 56 { MII_BCM54XX_EXP_DATA, 0xAB95 }, 57 /* increases internal 1V LDO voltage by 5% */ 58 { MII_BCM54XX_EXP_SEL, 0x2038 }, 59 { MII_BCM54XX_EXP_DATA, 0xBB22 }, 60 /* reduce RX low pass filter corner frequency */ 61 { MII_BCM54XX_EXP_SEL, 0x6038 }, 62 { MII_BCM54XX_EXP_DATA, 0xFFC5 }, 63 /* reduce RX high pass filter corner frequency */ 64 { MII_BCM54XX_EXP_SEL, 0x003a }, 65 { MII_BCM54XX_EXP_DATA, 0x2002 }, 66 }; 67 unsigned int i; 68 69 for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) { 70 ret = phy_write(phydev, 71 bcm7445_regs_cfg[i].reg, 72 bcm7445_regs_cfg[i].value); 73 if (ret) 74 return ret; 75 } 76 77 return 0; 78 } 79 80 static void phy_write_exp(struct phy_device *phydev, 81 u16 reg, u16 value) 82 { 83 phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); 84 phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 85 } 86 87 static void phy_write_misc(struct phy_device *phydev, 88 u16 reg, u16 chl, u16 value) 89 { 90 int tmp; 91 92 phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 93 94 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 95 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 96 phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 97 98 tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; 99 phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); 100 101 phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 102 } 103 104 static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev) 105 { 106 /* Increase VCO range to prevent unlocking problem of PLL at low 107 * temp 108 */ 109 phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 110 111 /* Change Ki to 011 */ 112 phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 113 114 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 115 * to 111 116 */ 117 phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 118 119 /* Adjust bias current trim by -3 */ 120 phy_write_misc(phydev, DSP_TAP10, 0x690b); 121 122 /* Switch to CORE_BASE1E */ 123 phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); 124 125 /* Reset R_CAL/RC_CAL Engine */ 126 phy_write_exp(phydev, CORE_EXPB0, 0x0010); 127 128 /* Disable Reset R_CAL/RC_CAL Engine */ 129 phy_write_exp(phydev, CORE_EXPB0, 0x0000); 130 131 /* write AFE_RXCONFIG_0 */ 132 phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 133 134 /* write AFE_RXCONFIG_1 */ 135 phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 136 137 /* write AFE_RX_LP_COUNTER */ 138 phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 139 140 /* write AFE_HPF_TRIM_OTHERS */ 141 phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 142 143 /* write AFTE_TX_CONFIG */ 144 phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 145 146 return 0; 147 } 148 149 static int bcm7xxx_28nm_config_init(struct phy_device *phydev) 150 { 151 int ret; 152 153 ret = bcm7445_config_init(phydev); 154 if (ret) 155 return ret; 156 157 return bcm7xxx_28nm_afe_config_init(phydev); 158 } 159 160 static int phy_set_clr_bits(struct phy_device *dev, int location, 161 int set_mask, int clr_mask) 162 { 163 int v, ret; 164 165 v = phy_read(dev, location); 166 if (v < 0) 167 return v; 168 169 v &= ~clr_mask; 170 v |= set_mask; 171 172 ret = phy_write(dev, location, v); 173 if (ret < 0) 174 return ret; 175 176 return v; 177 } 178 179 static int bcm7xxx_config_init(struct phy_device *phydev) 180 { 181 int ret; 182 183 /* Enable 64 clock MDIO */ 184 phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO); 185 phy_read(phydev, MII_BCM7XXX_AUX_MODE); 186 187 /* Workaround only required for 100Mbits/sec */ 188 if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR)) 189 return 0; 190 191 /* set shadow mode 2 */ 192 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 193 MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); 194 if (ret < 0) 195 return ret; 196 197 /* set iddq_clkbias */ 198 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); 199 udelay(10); 200 201 /* reset iddq_clkbias */ 202 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); 203 204 phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); 205 206 /* reset shadow mode 2 */ 207 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0); 208 if (ret < 0) 209 return ret; 210 211 return 0; 212 } 213 214 /* Workaround for putting the PHY in IDDQ mode, required 215 * for all BCM7XXX PHYs 216 */ 217 static int bcm7xxx_suspend(struct phy_device *phydev) 218 { 219 int ret; 220 const struct bcm7xxx_regs { 221 int reg; 222 u16 value; 223 } bcm7xxx_suspend_cfg[] = { 224 { MII_BCM7XXX_TEST, 0x008b }, 225 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, 226 { MII_BCM7XXX_100TX_DISC, 0x7000 }, 227 { MII_BCM7XXX_TEST, 0x000f }, 228 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, 229 { MII_BCM7XXX_TEST, 0x000b }, 230 }; 231 unsigned int i; 232 233 for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { 234 ret = phy_write(phydev, 235 bcm7xxx_suspend_cfg[i].reg, 236 bcm7xxx_suspend_cfg[i].value); 237 if (ret) 238 return ret; 239 } 240 241 return 0; 242 } 243 244 static int bcm7xxx_dummy_config_init(struct phy_device *phydev) 245 { 246 return 0; 247 } 248 249 static struct phy_driver bcm7xxx_driver[] = { 250 { 251 .phy_id = PHY_ID_BCM7366, 252 .phy_id_mask = 0xfffffff0, 253 .name = "Broadcom BCM7366", 254 .features = PHY_GBIT_FEATURES | 255 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 256 .flags = PHY_IS_INTERNAL, 257 .config_init = bcm7xxx_28nm_afe_config_init, 258 .config_aneg = genphy_config_aneg, 259 .read_status = genphy_read_status, 260 .suspend = bcm7xxx_suspend, 261 .resume = bcm7xxx_28nm_afe_config_init, 262 .driver = { .owner = THIS_MODULE }, 263 }, { 264 .phy_id = PHY_ID_BCM7439, 265 .phy_id_mask = 0xfffffff0, 266 .name = "Broadcom BCM7439", 267 .features = PHY_GBIT_FEATURES | 268 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 269 .flags = PHY_IS_INTERNAL, 270 .config_init = bcm7xxx_28nm_afe_config_init, 271 .config_aneg = genphy_config_aneg, 272 .read_status = genphy_read_status, 273 .suspend = bcm7xxx_suspend, 274 .resume = bcm7xxx_28nm_afe_config_init, 275 .driver = { .owner = THIS_MODULE }, 276 }, { 277 .phy_id = PHY_ID_BCM7445, 278 .phy_id_mask = 0xfffffff0, 279 .name = "Broadcom BCM7445", 280 .features = PHY_GBIT_FEATURES | 281 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 282 .flags = PHY_IS_INTERNAL, 283 .config_init = bcm7xxx_28nm_config_init, 284 .config_aneg = genphy_config_aneg, 285 .read_status = genphy_read_status, 286 .suspend = bcm7xxx_suspend, 287 .resume = bcm7xxx_28nm_config_init, 288 .driver = { .owner = THIS_MODULE }, 289 }, { 290 .name = "Broadcom BCM7XXX 28nm", 291 .phy_id = PHY_ID_BCM7XXX_28, 292 .phy_id_mask = PHY_BCM_OUI_MASK, 293 .features = PHY_GBIT_FEATURES | 294 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 295 .flags = PHY_IS_INTERNAL, 296 .config_init = bcm7xxx_28nm_config_init, 297 .config_aneg = genphy_config_aneg, 298 .read_status = genphy_read_status, 299 .suspend = bcm7xxx_suspend, 300 .resume = bcm7xxx_28nm_config_init, 301 .driver = { .owner = THIS_MODULE }, 302 }, { 303 .phy_id = PHY_BCM_OUI_4, 304 .phy_id_mask = 0xffff0000, 305 .name = "Broadcom BCM7XXX 40nm", 306 .features = PHY_GBIT_FEATURES | 307 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 308 .flags = PHY_IS_INTERNAL, 309 .config_init = bcm7xxx_config_init, 310 .config_aneg = genphy_config_aneg, 311 .read_status = genphy_read_status, 312 .suspend = bcm7xxx_suspend, 313 .resume = bcm7xxx_config_init, 314 .driver = { .owner = THIS_MODULE }, 315 }, { 316 .phy_id = PHY_BCM_OUI_5, 317 .phy_id_mask = 0xffffff00, 318 .name = "Broadcom BCM7XXX 65nm", 319 .features = PHY_BASIC_FEATURES | 320 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 321 .flags = PHY_IS_INTERNAL, 322 .config_init = bcm7xxx_dummy_config_init, 323 .config_aneg = genphy_config_aneg, 324 .read_status = genphy_read_status, 325 .suspend = bcm7xxx_suspend, 326 .resume = bcm7xxx_config_init, 327 .driver = { .owner = THIS_MODULE }, 328 } }; 329 330 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { 331 { PHY_ID_BCM7366, 0xfffffff0, }, 332 { PHY_ID_BCM7439, 0xfffffff0, }, 333 { PHY_ID_BCM7445, 0xfffffff0, }, 334 { PHY_ID_BCM7XXX_28, 0xfffffc00 }, 335 { PHY_BCM_OUI_4, 0xffff0000 }, 336 { PHY_BCM_OUI_5, 0xffffff00 }, 337 { } 338 }; 339 340 static int __init bcm7xxx_phy_init(void) 341 { 342 return phy_drivers_register(bcm7xxx_driver, 343 ARRAY_SIZE(bcm7xxx_driver)); 344 } 345 346 static void __exit bcm7xxx_phy_exit(void) 347 { 348 phy_drivers_unregister(bcm7xxx_driver, 349 ARRAY_SIZE(bcm7xxx_driver)); 350 } 351 352 module_init(bcm7xxx_phy_init); 353 module_exit(bcm7xxx_phy_exit); 354 355 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); 356 357 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); 358 MODULE_LICENSE("GPL"); 359 MODULE_AUTHOR("Broadcom Corporation"); 360