1b560a58cSFlorian Fainelli /* 2b560a58cSFlorian Fainelli * Broadcom BCM7xxx internal transceivers support. 3b560a58cSFlorian Fainelli * 4b560a58cSFlorian Fainelli * Copyright (C) 2014, Broadcom Corporation 5b560a58cSFlorian Fainelli * 6b560a58cSFlorian Fainelli * This program is free software; you can redistribute it and/or 7b560a58cSFlorian Fainelli * modify it under the terms of the GNU General Public License 8b560a58cSFlorian Fainelli * as published by the Free Software Foundation; either version 9b560a58cSFlorian Fainelli * 2 of the License, or (at your option) any later version. 10b560a58cSFlorian Fainelli */ 11b560a58cSFlorian Fainelli 12b560a58cSFlorian Fainelli #include <linux/module.h> 13b560a58cSFlorian Fainelli #include <linux/phy.h> 14b560a58cSFlorian Fainelli #include <linux/delay.h> 15b560a58cSFlorian Fainelli #include <linux/bitops.h> 16b560a58cSFlorian Fainelli #include <linux/brcmphy.h> 17b8f9a029SFlorian Fainelli #include <linux/mdio.h> 18b560a58cSFlorian Fainelli 19b560a58cSFlorian Fainelli /* Broadcom BCM7xxx internal PHY registers */ 20b560a58cSFlorian Fainelli #define MII_BCM7XXX_CHANNEL_WIDTH 0x2000 21b560a58cSFlorian Fainelli 22b560a58cSFlorian Fainelli /* 40nm only register definitions */ 23b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_AUX_CTL 0x10 24b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 25b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_DISC 0x14 26b560a58cSFlorian Fainelli #define MII_BCM7XXX_AUX_MODE 0x1d 27b560a58cSFlorian Fainelli #define MII_BCM7XX_64CLK_MDIO BIT(12) 28b560a58cSFlorian Fainelli #define MII_BCM7XXX_CORE_BASE1E 0x1e 29b560a58cSFlorian Fainelli #define MII_BCM7XXX_TEST 0x1f 30b560a58cSFlorian Fainelli #define MII_BCM7XXX_SHD_MODE_2 BIT(2) 31b560a58cSFlorian Fainelli 32a3622f2cSFlorian Fainelli /* 28nm only register definitions */ 33a3622f2cSFlorian Fainelli #define MISC_ADDR(base, channel) base, channel 34a3622f2cSFlorian Fainelli 35a3622f2cSFlorian Fainelli #define DSP_TAP10 MISC_ADDR(0x0a, 0) 36a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) 37a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) 38a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) 39a3622f2cSFlorian Fainelli 40a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) 41a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) 42a490631fSFlorian Fainelli #define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) 43a3622f2cSFlorian Fainelli #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) 44a3622f2cSFlorian Fainelli #define AFE_TX_CONFIG MISC_ADDR(0x39, 0) 45a490631fSFlorian Fainelli #define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) 46a490631fSFlorian Fainelli #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) 47a3622f2cSFlorian Fainelli #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) 48a3622f2cSFlorian Fainelli 49a3622f2cSFlorian Fainelli #define CORE_EXPB0 0xb0 50a3622f2cSFlorian Fainelli 51b560a58cSFlorian Fainelli static void phy_write_exp(struct phy_device *phydev, 52b560a58cSFlorian Fainelli u16 reg, u16 value) 53b560a58cSFlorian Fainelli { 54b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); 55b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 56b560a58cSFlorian Fainelli } 57b560a58cSFlorian Fainelli 58b560a58cSFlorian Fainelli static void phy_write_misc(struct phy_device *phydev, 59b560a58cSFlorian Fainelli u16 reg, u16 chl, u16 value) 60b560a58cSFlorian Fainelli { 61b560a58cSFlorian Fainelli int tmp; 62b560a58cSFlorian Fainelli 63b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 64b560a58cSFlorian Fainelli 65b560a58cSFlorian Fainelli tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 66b560a58cSFlorian Fainelli tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 67b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 68b560a58cSFlorian Fainelli 69b560a58cSFlorian Fainelli tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; 70b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); 71b560a58cSFlorian Fainelli 72b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM54XX_EXP_DATA, value); 73b560a58cSFlorian Fainelli } 74b560a58cSFlorian Fainelli 759c41f2baSFlorian Fainelli static void r_rc_cal_reset(struct phy_device *phydev) 769c41f2baSFlorian Fainelli { 779c41f2baSFlorian Fainelli /* Reset R_CAL/RC_CAL Engine */ 789c41f2baSFlorian Fainelli phy_write_exp(phydev, 0x00b0, 0x0010); 799c41f2baSFlorian Fainelli 809c41f2baSFlorian Fainelli /* Disable Reset R_AL/RC_CAL Engine */ 819c41f2baSFlorian Fainelli phy_write_exp(phydev, 0x00b0, 0x0000); 829c41f2baSFlorian Fainelli } 839c41f2baSFlorian Fainelli 842a9df742SFlorian Fainelli static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) 85b560a58cSFlorian Fainelli { 86b560a58cSFlorian Fainelli /* Increase VCO range to prevent unlocking problem of PLL at low 87b560a58cSFlorian Fainelli * temp 88b560a58cSFlorian Fainelli */ 89a3622f2cSFlorian Fainelli phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 90b560a58cSFlorian Fainelli 91b560a58cSFlorian Fainelli /* Change Ki to 011 */ 92a3622f2cSFlorian Fainelli phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 93b560a58cSFlorian Fainelli 94b560a58cSFlorian Fainelli /* Disable loading of TVCO buffer to bandgap, set bandgap trim 95b560a58cSFlorian Fainelli * to 111 96b560a58cSFlorian Fainelli */ 97a3622f2cSFlorian Fainelli phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 98b560a58cSFlorian Fainelli 99b560a58cSFlorian Fainelli /* Adjust bias current trim by -3 */ 100a3622f2cSFlorian Fainelli phy_write_misc(phydev, DSP_TAP10, 0x690b); 101b560a58cSFlorian Fainelli 102b560a58cSFlorian Fainelli /* Switch to CORE_BASE1E */ 103b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); 104b560a58cSFlorian Fainelli 1059c41f2baSFlorian Fainelli r_rc_cal_reset(phydev); 106b560a58cSFlorian Fainelli 1079918542eSFlorian Fainelli /* write AFE_RXCONFIG_0 */ 1089918542eSFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 1099918542eSFlorian Fainelli 1109918542eSFlorian Fainelli /* write AFE_RXCONFIG_1 */ 1119918542eSFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 1129918542eSFlorian Fainelli 1139918542eSFlorian Fainelli /* write AFE_RX_LP_COUNTER */ 114a62ea5a7SFlorian Fainelli phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 1159918542eSFlorian Fainelli 1169918542eSFlorian Fainelli /* write AFE_HPF_TRIM_OTHERS */ 1179918542eSFlorian Fainelli phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 1189918542eSFlorian Fainelli 1199918542eSFlorian Fainelli /* write AFTE_TX_CONFIG */ 1209918542eSFlorian Fainelli phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 1219918542eSFlorian Fainelli 122b560a58cSFlorian Fainelli return 0; 123b560a58cSFlorian Fainelli } 124b560a58cSFlorian Fainelli 125a490631fSFlorian Fainelli static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) 126a490631fSFlorian Fainelli { 127a490631fSFlorian Fainelli /* AFE_RXCONFIG_0 */ 128a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); 129a490631fSFlorian Fainelli 130a490631fSFlorian Fainelli /* AFE_RXCONFIG_1 */ 131a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 132a490631fSFlorian Fainelli 133a490631fSFlorian Fainelli /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ 134a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); 135a490631fSFlorian Fainelli 136a490631fSFlorian Fainelli /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ 137a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 138a490631fSFlorian Fainelli 139a490631fSFlorian Fainelli /* AFE_TX_CONFIG, set 1000BT Cfeed=110 for all ports */ 140a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_TX_CONFIG, 0x0061); 141a490631fSFlorian Fainelli 142a490631fSFlorian Fainelli /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 143a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 144a490631fSFlorian Fainelli 145a490631fSFlorian Fainelli /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ 146a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); 147a490631fSFlorian Fainelli 148a490631fSFlorian Fainelli /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 149a490631fSFlorian Fainelli * offset for HT=0 code 150a490631fSFlorian Fainelli */ 151a490631fSFlorian Fainelli phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 152a490631fSFlorian Fainelli 153a490631fSFlorian Fainelli /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 154a490631fSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); 155a490631fSFlorian Fainelli 156a490631fSFlorian Fainelli /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 157a490631fSFlorian Fainelli phy_write_misc(phydev, DSP_TAP10, 0x011b); 158a490631fSFlorian Fainelli 159a490631fSFlorian Fainelli /* Reset R_CAL/RC_CAL engine */ 160a490631fSFlorian Fainelli r_rc_cal_reset(phydev); 161a490631fSFlorian Fainelli 162a490631fSFlorian Fainelli return 0; 163a490631fSFlorian Fainelli } 164a490631fSFlorian Fainelli 1650c2fdc25SFlorian Fainelli static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) 1660c2fdc25SFlorian Fainelli { 1670c2fdc25SFlorian Fainelli /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ 1680c2fdc25SFlorian Fainelli phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); 1690c2fdc25SFlorian Fainelli 1700c2fdc25SFlorian Fainelli /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ 1710c2fdc25SFlorian Fainelli phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); 1720c2fdc25SFlorian Fainelli 1730c2fdc25SFlorian Fainelli /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal 1740c2fdc25SFlorian Fainelli * offset for HT=0 code 1750c2fdc25SFlorian Fainelli */ 1760c2fdc25SFlorian Fainelli phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); 1770c2fdc25SFlorian Fainelli 1780c2fdc25SFlorian Fainelli /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ 1790c2fdc25SFlorian Fainelli phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); 1800c2fdc25SFlorian Fainelli 1810c2fdc25SFlorian Fainelli /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ 1820c2fdc25SFlorian Fainelli phy_write_misc(phydev, DSP_TAP10, 0x011b); 1830c2fdc25SFlorian Fainelli 1840c2fdc25SFlorian Fainelli /* Reset R_CAL/RC_CAL engine */ 1850c2fdc25SFlorian Fainelli r_rc_cal_reset(phydev); 1860c2fdc25SFlorian Fainelli 1870c2fdc25SFlorian Fainelli return 0; 1880c2fdc25SFlorian Fainelli } 1890c2fdc25SFlorian Fainelli 1909df54ddaSFlorian Fainelli static int bcm7xxx_apd_enable(struct phy_device *phydev) 1919df54ddaSFlorian Fainelli { 1929df54ddaSFlorian Fainelli int val; 1939df54ddaSFlorian Fainelli 1949df54ddaSFlorian Fainelli /* Enable powering down of the DLL during auto-power down */ 1959df54ddaSFlorian Fainelli val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); 1969df54ddaSFlorian Fainelli if (val < 0) 1979df54ddaSFlorian Fainelli return val; 1989df54ddaSFlorian Fainelli 1999df54ddaSFlorian Fainelli val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 2009df54ddaSFlorian Fainelli bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); 2019df54ddaSFlorian Fainelli 2029df54ddaSFlorian Fainelli /* Enable auto-power down */ 2039df54ddaSFlorian Fainelli val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); 2049df54ddaSFlorian Fainelli if (val < 0) 2059df54ddaSFlorian Fainelli return val; 2069df54ddaSFlorian Fainelli 2079df54ddaSFlorian Fainelli val |= BCM54XX_SHD_APD_EN; 2089df54ddaSFlorian Fainelli return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); 2099df54ddaSFlorian Fainelli } 2109df54ddaSFlorian Fainelli 211b8f9a029SFlorian Fainelli static int bcm7xxx_eee_enable(struct phy_device *phydev) 212b8f9a029SFlorian Fainelli { 213b8f9a029SFlorian Fainelli int val; 214b8f9a029SFlorian Fainelli 215b8f9a029SFlorian Fainelli val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, 216b8f9a029SFlorian Fainelli MDIO_MMD_AN, phydev->addr); 217b8f9a029SFlorian Fainelli if (val < 0) 218b8f9a029SFlorian Fainelli return val; 219b8f9a029SFlorian Fainelli 220b8f9a029SFlorian Fainelli /* Enable general EEE feature at the PHY level */ 221b8f9a029SFlorian Fainelli val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 222b8f9a029SFlorian Fainelli 223b8f9a029SFlorian Fainelli phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, 224b8f9a029SFlorian Fainelli MDIO_MMD_AN, phydev->addr, val); 225b8f9a029SFlorian Fainelli 226b8f9a029SFlorian Fainelli /* Advertise supported modes */ 227b8f9a029SFlorian Fainelli val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, 228b8f9a029SFlorian Fainelli MDIO_MMD_AN, phydev->addr); 229b8f9a029SFlorian Fainelli 230b8f9a029SFlorian Fainelli val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); 231b8f9a029SFlorian Fainelli phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, 232b8f9a029SFlorian Fainelli MDIO_MMD_AN, phydev->addr, val); 233b8f9a029SFlorian Fainelli 234b8f9a029SFlorian Fainelli return 0; 235b8f9a029SFlorian Fainelli } 236b8f9a029SFlorian Fainelli 237b560a58cSFlorian Fainelli static int bcm7xxx_28nm_config_init(struct phy_device *phydev) 238b560a58cSFlorian Fainelli { 239d8ebfed3SFlorian Fainelli u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); 240d8ebfed3SFlorian Fainelli u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); 241d8ebfed3SFlorian Fainelli int ret = 0; 242b560a58cSFlorian Fainelli 2436ec259c1SFlorian Fainelli pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", 2446ec259c1SFlorian Fainelli dev_name(&phydev->dev), phydev->drv->name, rev, patch); 245d8ebfed3SFlorian Fainelli 246d8ebfed3SFlorian Fainelli switch (rev) { 247d8ebfed3SFlorian Fainelli case 0xb0: 2482a9df742SFlorian Fainelli ret = bcm7xxx_28nm_b0_afe_config_init(phydev); 249d8ebfed3SFlorian Fainelli break; 250a490631fSFlorian Fainelli case 0xd0: 251a490631fSFlorian Fainelli ret = bcm7xxx_28nm_d0_afe_config_init(phydev); 252a490631fSFlorian Fainelli break; 2530c2fdc25SFlorian Fainelli case 0xe0: 2540c2fdc25SFlorian Fainelli case 0xf0: 25560efff0cSFlorian Fainelli /* Rev G0 introduces a roll over */ 25660efff0cSFlorian Fainelli case 0x10: 2570c2fdc25SFlorian Fainelli ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev); 2580c2fdc25SFlorian Fainelli break; 259d8ebfed3SFlorian Fainelli default: 260d8ebfed3SFlorian Fainelli break; 261d8ebfed3SFlorian Fainelli } 262d8ebfed3SFlorian Fainelli 2639df54ddaSFlorian Fainelli if (ret) 2649df54ddaSFlorian Fainelli return ret; 2659df54ddaSFlorian Fainelli 266b8f9a029SFlorian Fainelli ret = bcm7xxx_eee_enable(phydev); 267b8f9a029SFlorian Fainelli if (ret) 268b8f9a029SFlorian Fainelli return ret; 269b8f9a029SFlorian Fainelli 2709df54ddaSFlorian Fainelli return bcm7xxx_apd_enable(phydev); 271b560a58cSFlorian Fainelli } 272b560a58cSFlorian Fainelli 2734fd14e0bSFlorian Fainelli static int bcm7xxx_28nm_resume(struct phy_device *phydev) 2744fd14e0bSFlorian Fainelli { 2754fd14e0bSFlorian Fainelli int ret; 2764fd14e0bSFlorian Fainelli 2774fd14e0bSFlorian Fainelli /* Re-apply workarounds coming out suspend/resume */ 2784fd14e0bSFlorian Fainelli ret = bcm7xxx_28nm_config_init(phydev); 2794fd14e0bSFlorian Fainelli if (ret) 2804fd14e0bSFlorian Fainelli return ret; 2814fd14e0bSFlorian Fainelli 2824fd14e0bSFlorian Fainelli /* 28nm Gigabit PHYs come out of reset without any half-duplex 2834fd14e0bSFlorian Fainelli * or "hub" compliant advertised mode, fix that. This does not 2844fd14e0bSFlorian Fainelli * cause any problems with the PHY library since genphy_config_aneg() 2854fd14e0bSFlorian Fainelli * gracefully handles auto-negotiated and forced modes. 2864fd14e0bSFlorian Fainelli */ 2874fd14e0bSFlorian Fainelli return genphy_config_aneg(phydev); 2884fd14e0bSFlorian Fainelli } 2894fd14e0bSFlorian Fainelli 290b560a58cSFlorian Fainelli static int phy_set_clr_bits(struct phy_device *dev, int location, 291b560a58cSFlorian Fainelli int set_mask, int clr_mask) 292b560a58cSFlorian Fainelli { 293b560a58cSFlorian Fainelli int v, ret; 294b560a58cSFlorian Fainelli 295b560a58cSFlorian Fainelli v = phy_read(dev, location); 296b560a58cSFlorian Fainelli if (v < 0) 297b560a58cSFlorian Fainelli return v; 298b560a58cSFlorian Fainelli 299b560a58cSFlorian Fainelli v &= ~clr_mask; 300b560a58cSFlorian Fainelli v |= set_mask; 301b560a58cSFlorian Fainelli 302b560a58cSFlorian Fainelli ret = phy_write(dev, location, v); 303b560a58cSFlorian Fainelli if (ret < 0) 304b560a58cSFlorian Fainelli return ret; 305b560a58cSFlorian Fainelli 306b560a58cSFlorian Fainelli return v; 307b560a58cSFlorian Fainelli } 308b560a58cSFlorian Fainelli 309b560a58cSFlorian Fainelli static int bcm7xxx_config_init(struct phy_device *phydev) 310b560a58cSFlorian Fainelli { 311b560a58cSFlorian Fainelli int ret; 312b560a58cSFlorian Fainelli 313b560a58cSFlorian Fainelli /* Enable 64 clock MDIO */ 314b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO); 315b560a58cSFlorian Fainelli phy_read(phydev, MII_BCM7XXX_AUX_MODE); 316b560a58cSFlorian Fainelli 317e18556eeSFlorian Fainelli /* Workaround only required for 100Mbits/sec capable PHYs */ 318e18556eeSFlorian Fainelli if (phydev->supported & PHY_GBIT_FEATURES) 319b560a58cSFlorian Fainelli return 0; 320b560a58cSFlorian Fainelli 321b560a58cSFlorian Fainelli /* set shadow mode 2 */ 322b560a58cSFlorian Fainelli ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 323b560a58cSFlorian Fainelli MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); 324b560a58cSFlorian Fainelli if (ret < 0) 325b560a58cSFlorian Fainelli return ret; 326b560a58cSFlorian Fainelli 327b560a58cSFlorian Fainelli /* set iddq_clkbias */ 328b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); 329b560a58cSFlorian Fainelli udelay(10); 330b560a58cSFlorian Fainelli 331b560a58cSFlorian Fainelli /* reset iddq_clkbias */ 332b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); 333b560a58cSFlorian Fainelli 334b560a58cSFlorian Fainelli phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); 335b560a58cSFlorian Fainelli 336b560a58cSFlorian Fainelli /* reset shadow mode 2 */ 337b560a58cSFlorian Fainelli ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0); 338b560a58cSFlorian Fainelli if (ret < 0) 339b560a58cSFlorian Fainelli return ret; 340b560a58cSFlorian Fainelli 341b560a58cSFlorian Fainelli return 0; 342b560a58cSFlorian Fainelli } 343b560a58cSFlorian Fainelli 344b560a58cSFlorian Fainelli /* Workaround for putting the PHY in IDDQ mode, required 34582c084f5SFlorian Fainelli * for all BCM7XXX 40nm and 65nm PHYs 346b560a58cSFlorian Fainelli */ 347b560a58cSFlorian Fainelli static int bcm7xxx_suspend(struct phy_device *phydev) 348b560a58cSFlorian Fainelli { 349b560a58cSFlorian Fainelli int ret; 350b560a58cSFlorian Fainelli const struct bcm7xxx_regs { 351b560a58cSFlorian Fainelli int reg; 352b560a58cSFlorian Fainelli u16 value; 353b560a58cSFlorian Fainelli } bcm7xxx_suspend_cfg[] = { 354b560a58cSFlorian Fainelli { MII_BCM7XXX_TEST, 0x008b }, 355b560a58cSFlorian Fainelli { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, 356b560a58cSFlorian Fainelli { MII_BCM7XXX_100TX_DISC, 0x7000 }, 357b560a58cSFlorian Fainelli { MII_BCM7XXX_TEST, 0x000f }, 358b560a58cSFlorian Fainelli { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, 359b560a58cSFlorian Fainelli { MII_BCM7XXX_TEST, 0x000b }, 360b560a58cSFlorian Fainelli }; 361b560a58cSFlorian Fainelli unsigned int i; 362b560a58cSFlorian Fainelli 363b560a58cSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { 364b560a58cSFlorian Fainelli ret = phy_write(phydev, 365b560a58cSFlorian Fainelli bcm7xxx_suspend_cfg[i].reg, 366b560a58cSFlorian Fainelli bcm7xxx_suspend_cfg[i].value); 367b560a58cSFlorian Fainelli if (ret) 368b560a58cSFlorian Fainelli return ret; 369b560a58cSFlorian Fainelli } 370b560a58cSFlorian Fainelli 371b560a58cSFlorian Fainelli return 0; 372b560a58cSFlorian Fainelli } 373b560a58cSFlorian Fainelli 374b560a58cSFlorian Fainelli static int bcm7xxx_dummy_config_init(struct phy_device *phydev) 375b560a58cSFlorian Fainelli { 376b560a58cSFlorian Fainelli return 0; 377b560a58cSFlorian Fainelli } 378b560a58cSFlorian Fainelli 379153df3c7SFlorian Fainelli #define BCM7XXX_28NM_GPHY(_oui, _name) \ 380153df3c7SFlorian Fainelli { \ 381153df3c7SFlorian Fainelli .phy_id = (_oui), \ 382153df3c7SFlorian Fainelli .phy_id_mask = 0xfffffff0, \ 383153df3c7SFlorian Fainelli .name = _name, \ 384153df3c7SFlorian Fainelli .features = PHY_GBIT_FEATURES | \ 385153df3c7SFlorian Fainelli SUPPORTED_Pause | SUPPORTED_Asym_Pause, \ 386153df3c7SFlorian Fainelli .flags = PHY_IS_INTERNAL, \ 3872a9df742SFlorian Fainelli .config_init = bcm7xxx_28nm_config_init, \ 388153df3c7SFlorian Fainelli .config_aneg = genphy_config_aneg, \ 389153df3c7SFlorian Fainelli .read_status = genphy_read_status, \ 390153df3c7SFlorian Fainelli .resume = bcm7xxx_28nm_resume, \ 391153df3c7SFlorian Fainelli .driver = { .owner = THIS_MODULE }, \ 392153df3c7SFlorian Fainelli } 393153df3c7SFlorian Fainelli 394b560a58cSFlorian Fainelli static struct phy_driver bcm7xxx_driver[] = { 395430ad68fSFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), 396430ad68fSFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), 397153df3c7SFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), 398153df3c7SFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), 39959e33c2bSFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), 400153df3c7SFlorian Fainelli BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), 401b560a58cSFlorian Fainelli { 402d068b02cSPetri Gynther .phy_id = PHY_ID_BCM7425, 403d068b02cSPetri Gynther .phy_id_mask = 0xfffffff0, 404d068b02cSPetri Gynther .name = "Broadcom BCM7425", 405d068b02cSPetri Gynther .features = PHY_GBIT_FEATURES | 406d068b02cSPetri Gynther SUPPORTED_Pause | SUPPORTED_Asym_Pause, 407d068b02cSPetri Gynther .flags = 0, 408d068b02cSPetri Gynther .config_init = bcm7xxx_config_init, 409d068b02cSPetri Gynther .config_aneg = genphy_config_aneg, 410d068b02cSPetri Gynther .read_status = genphy_read_status, 411d068b02cSPetri Gynther .suspend = bcm7xxx_suspend, 412d068b02cSPetri Gynther .resume = bcm7xxx_config_init, 413d068b02cSPetri Gynther .driver = { .owner = THIS_MODULE }, 414d068b02cSPetri Gynther }, { 415d068b02cSPetri Gynther .phy_id = PHY_ID_BCM7429, 416d068b02cSPetri Gynther .phy_id_mask = 0xfffffff0, 417d068b02cSPetri Gynther .name = "Broadcom BCM7429", 418d068b02cSPetri Gynther .features = PHY_GBIT_FEATURES | 419d068b02cSPetri Gynther SUPPORTED_Pause | SUPPORTED_Asym_Pause, 420d068b02cSPetri Gynther .flags = PHY_IS_INTERNAL, 421d068b02cSPetri Gynther .config_init = bcm7xxx_config_init, 422d068b02cSPetri Gynther .config_aneg = genphy_config_aneg, 423d068b02cSPetri Gynther .read_status = genphy_read_status, 424d068b02cSPetri Gynther .suspend = bcm7xxx_suspend, 425d068b02cSPetri Gynther .resume = bcm7xxx_config_init, 426d068b02cSPetri Gynther .driver = { .owner = THIS_MODULE }, 427d068b02cSPetri Gynther }, { 428b560a58cSFlorian Fainelli .phy_id = PHY_BCM_OUI_4, 429b560a58cSFlorian Fainelli .phy_id_mask = 0xffff0000, 430b560a58cSFlorian Fainelli .name = "Broadcom BCM7XXX 40nm", 431b560a58cSFlorian Fainelli .features = PHY_GBIT_FEATURES | 432b560a58cSFlorian Fainelli SUPPORTED_Pause | SUPPORTED_Asym_Pause, 433b560a58cSFlorian Fainelli .flags = PHY_IS_INTERNAL, 434b560a58cSFlorian Fainelli .config_init = bcm7xxx_config_init, 435b560a58cSFlorian Fainelli .config_aneg = genphy_config_aneg, 436b560a58cSFlorian Fainelli .read_status = genphy_read_status, 437b560a58cSFlorian Fainelli .suspend = bcm7xxx_suspend, 438b560a58cSFlorian Fainelli .resume = bcm7xxx_config_init, 439b560a58cSFlorian Fainelli .driver = { .owner = THIS_MODULE }, 440b560a58cSFlorian Fainelli }, { 441b560a58cSFlorian Fainelli .phy_id = PHY_BCM_OUI_5, 442b560a58cSFlorian Fainelli .phy_id_mask = 0xffffff00, 443b560a58cSFlorian Fainelli .name = "Broadcom BCM7XXX 65nm", 444b560a58cSFlorian Fainelli .features = PHY_BASIC_FEATURES | 445b560a58cSFlorian Fainelli SUPPORTED_Pause | SUPPORTED_Asym_Pause, 446b560a58cSFlorian Fainelli .flags = PHY_IS_INTERNAL, 447b560a58cSFlorian Fainelli .config_init = bcm7xxx_dummy_config_init, 448b560a58cSFlorian Fainelli .config_aneg = genphy_config_aneg, 449b560a58cSFlorian Fainelli .read_status = genphy_read_status, 450b560a58cSFlorian Fainelli .suspend = bcm7xxx_suspend, 451b560a58cSFlorian Fainelli .resume = bcm7xxx_config_init, 452b560a58cSFlorian Fainelli .driver = { .owner = THIS_MODULE }, 453b560a58cSFlorian Fainelli } }; 454b560a58cSFlorian Fainelli 455b560a58cSFlorian Fainelli static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { 456430ad68fSFlorian Fainelli { PHY_ID_BCM7250, 0xfffffff0, }, 457430ad68fSFlorian Fainelli { PHY_ID_BCM7364, 0xfffffff0, }, 458b560a58cSFlorian Fainelli { PHY_ID_BCM7366, 0xfffffff0, }, 459d068b02cSPetri Gynther { PHY_ID_BCM7425, 0xfffffff0, }, 460d068b02cSPetri Gynther { PHY_ID_BCM7429, 0xfffffff0, }, 461b560a58cSFlorian Fainelli { PHY_ID_BCM7439, 0xfffffff0, }, 462b560a58cSFlorian Fainelli { PHY_ID_BCM7445, 0xfffffff0, }, 463b560a58cSFlorian Fainelli { PHY_BCM_OUI_4, 0xffff0000 }, 464b560a58cSFlorian Fainelli { PHY_BCM_OUI_5, 0xffffff00 }, 465b560a58cSFlorian Fainelli { } 466b560a58cSFlorian Fainelli }; 467b560a58cSFlorian Fainelli 46850fd7150SJohan Hovold module_phy_driver(bcm7xxx_driver); 469b560a58cSFlorian Fainelli 470b560a58cSFlorian Fainelli MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); 471b560a58cSFlorian Fainelli 472b560a58cSFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); 473b560a58cSFlorian Fainelli MODULE_LICENSE("GPL"); 474b560a58cSFlorian Fainelli MODULE_AUTHOR("Broadcom Corporation"); 475