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 v3.17-rc7 359 lines 9.4 kB view raw
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 47static 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 80static 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 87static 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 104static 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 149static 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 160static int bcm7xxx_28nm_resume(struct phy_device *phydev) 161{ 162 int ret; 163 164 /* Re-apply workarounds coming out suspend/resume */ 165 ret = bcm7xxx_28nm_config_init(phydev); 166 if (ret) 167 return ret; 168 169 /* 28nm Gigabit PHYs come out of reset without any half-duplex 170 * or "hub" compliant advertised mode, fix that. This does not 171 * cause any problems with the PHY library since genphy_config_aneg() 172 * gracefully handles auto-negotiated and forced modes. 173 */ 174 return genphy_config_aneg(phydev); 175} 176 177static int phy_set_clr_bits(struct phy_device *dev, int location, 178 int set_mask, int clr_mask) 179{ 180 int v, ret; 181 182 v = phy_read(dev, location); 183 if (v < 0) 184 return v; 185 186 v &= ~clr_mask; 187 v |= set_mask; 188 189 ret = phy_write(dev, location, v); 190 if (ret < 0) 191 return ret; 192 193 return v; 194} 195 196static int bcm7xxx_config_init(struct phy_device *phydev) 197{ 198 int ret; 199 200 /* Enable 64 clock MDIO */ 201 phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO); 202 phy_read(phydev, MII_BCM7XXX_AUX_MODE); 203 204 /* Workaround only required for 100Mbits/sec */ 205 if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR)) 206 return 0; 207 208 /* set shadow mode 2 */ 209 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 210 MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); 211 if (ret < 0) 212 return ret; 213 214 /* set iddq_clkbias */ 215 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); 216 udelay(10); 217 218 /* reset iddq_clkbias */ 219 phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); 220 221 phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); 222 223 /* reset shadow mode 2 */ 224 ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0); 225 if (ret < 0) 226 return ret; 227 228 return 0; 229} 230 231/* Workaround for putting the PHY in IDDQ mode, required 232 * for all BCM7XXX 40nm and 65nm PHYs 233 */ 234static int bcm7xxx_suspend(struct phy_device *phydev) 235{ 236 int ret; 237 const struct bcm7xxx_regs { 238 int reg; 239 u16 value; 240 } bcm7xxx_suspend_cfg[] = { 241 { MII_BCM7XXX_TEST, 0x008b }, 242 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, 243 { MII_BCM7XXX_100TX_DISC, 0x7000 }, 244 { MII_BCM7XXX_TEST, 0x000f }, 245 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, 246 { MII_BCM7XXX_TEST, 0x000b }, 247 }; 248 unsigned int i; 249 250 for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { 251 ret = phy_write(phydev, 252 bcm7xxx_suspend_cfg[i].reg, 253 bcm7xxx_suspend_cfg[i].value); 254 if (ret) 255 return ret; 256 } 257 258 return 0; 259} 260 261static int bcm7xxx_dummy_config_init(struct phy_device *phydev) 262{ 263 return 0; 264} 265 266static struct phy_driver bcm7xxx_driver[] = { 267{ 268 .phy_id = PHY_ID_BCM7366, 269 .phy_id_mask = 0xfffffff0, 270 .name = "Broadcom BCM7366", 271 .features = PHY_GBIT_FEATURES | 272 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 273 .flags = PHY_IS_INTERNAL, 274 .config_init = bcm7xxx_28nm_afe_config_init, 275 .config_aneg = genphy_config_aneg, 276 .read_status = genphy_read_status, 277 .resume = bcm7xxx_28nm_resume, 278 .driver = { .owner = THIS_MODULE }, 279}, { 280 .phy_id = PHY_ID_BCM7439, 281 .phy_id_mask = 0xfffffff0, 282 .name = "Broadcom BCM7439", 283 .features = PHY_GBIT_FEATURES | 284 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 285 .flags = PHY_IS_INTERNAL, 286 .config_init = bcm7xxx_28nm_afe_config_init, 287 .config_aneg = genphy_config_aneg, 288 .read_status = genphy_read_status, 289 .resume = bcm7xxx_28nm_resume, 290 .driver = { .owner = THIS_MODULE }, 291}, { 292 .phy_id = PHY_ID_BCM7445, 293 .phy_id_mask = 0xfffffff0, 294 .name = "Broadcom BCM7445", 295 .features = PHY_GBIT_FEATURES | 296 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 297 .flags = PHY_IS_INTERNAL, 298 .config_init = bcm7xxx_28nm_config_init, 299 .config_aneg = genphy_config_aneg, 300 .read_status = genphy_read_status, 301 .resume = bcm7xxx_28nm_afe_config_init, 302 .driver = { .owner = THIS_MODULE }, 303}, { 304 .phy_id = PHY_BCM_OUI_4, 305 .phy_id_mask = 0xffff0000, 306 .name = "Broadcom BCM7XXX 40nm", 307 .features = PHY_GBIT_FEATURES | 308 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 309 .flags = PHY_IS_INTERNAL, 310 .config_init = bcm7xxx_config_init, 311 .config_aneg = genphy_config_aneg, 312 .read_status = genphy_read_status, 313 .suspend = bcm7xxx_suspend, 314 .resume = bcm7xxx_config_init, 315 .driver = { .owner = THIS_MODULE }, 316}, { 317 .phy_id = PHY_BCM_OUI_5, 318 .phy_id_mask = 0xffffff00, 319 .name = "Broadcom BCM7XXX 65nm", 320 .features = PHY_BASIC_FEATURES | 321 SUPPORTED_Pause | SUPPORTED_Asym_Pause, 322 .flags = PHY_IS_INTERNAL, 323 .config_init = bcm7xxx_dummy_config_init, 324 .config_aneg = genphy_config_aneg, 325 .read_status = genphy_read_status, 326 .suspend = bcm7xxx_suspend, 327 .resume = bcm7xxx_config_init, 328 .driver = { .owner = THIS_MODULE }, 329} }; 330 331static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { 332 { PHY_ID_BCM7366, 0xfffffff0, }, 333 { PHY_ID_BCM7439, 0xfffffff0, }, 334 { PHY_ID_BCM7445, 0xfffffff0, }, 335 { PHY_BCM_OUI_4, 0xffff0000 }, 336 { PHY_BCM_OUI_5, 0xffffff00 }, 337 { } 338}; 339 340static int __init bcm7xxx_phy_init(void) 341{ 342 return phy_drivers_register(bcm7xxx_driver, 343 ARRAY_SIZE(bcm7xxx_driver)); 344} 345 346static void __exit bcm7xxx_phy_exit(void) 347{ 348 phy_drivers_unregister(bcm7xxx_driver, 349 ARRAY_SIZE(bcm7xxx_driver)); 350} 351 352module_init(bcm7xxx_phy_init); 353module_exit(bcm7xxx_phy_exit); 354 355MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); 356 357MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); 358MODULE_LICENSE("GPL"); 359MODULE_AUTHOR("Broadcom Corporation");