1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2015 Broadcom Corporation 4 */ 5 6 /* Broadcom Cygnus SoC internal transceivers support. */ 7 #include "bcm-phy-lib.h" 8 #include <linux/brcmphy.h> 9 #include <linux/module.h> 10 #include <linux/netdevice.h> 11 #include <linux/phy.h> 12 13 struct bcm_omega_phy_priv { 14 u64 *stats; 15 }; 16 17 /* Broadcom Cygnus Phy specific registers */ 18 #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */ 19 20 static int bcm_cygnus_afe_config(struct phy_device *phydev) 21 { 22 int rc; 23 24 /* ensure smdspclk is enabled */ 25 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); 26 if (rc < 0) 27 return rc; 28 29 /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ 30 rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); 31 if (rc < 0) 32 return rc; 33 34 /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ 35 rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); 36 if (rc < 0) 37 return rc; 38 39 /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ 40 rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); 41 if (rc < 0) 42 return rc; 43 44 /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ 45 rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); 46 if (rc < 0) 47 return rc; 48 49 /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ 50 rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); 51 if (rc < 0) 52 return rc; 53 54 /* Adjust bias current trim to overcome digital offSet */ 55 rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); 56 if (rc < 0) 57 return rc; 58 59 /* make rcal=100, since rdb default is 000 */ 60 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10); 61 if (rc < 0) 62 return rc; 63 64 /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ 65 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10); 66 if (rc < 0) 67 return rc; 68 69 /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ 70 rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00); 71 72 return 0; 73 } 74 75 static int bcm_cygnus_config_init(struct phy_device *phydev) 76 { 77 int reg, rc; 78 79 reg = phy_read(phydev, MII_BCM54XX_ECR); 80 if (reg < 0) 81 return reg; 82 83 /* Mask interrupts globally. */ 84 reg |= MII_BCM54XX_ECR_IM; 85 rc = phy_write(phydev, MII_BCM54XX_ECR, reg); 86 if (rc) 87 return rc; 88 89 /* Unmask events of interest */ 90 reg = ~(MII_BCM54XX_INT_DUPLEX | 91 MII_BCM54XX_INT_SPEED | 92 MII_BCM54XX_INT_LINK); 93 rc = phy_write(phydev, MII_BCM54XX_IMR, reg); 94 if (rc) 95 return rc; 96 97 /* Apply AFE settings for the PHY */ 98 rc = bcm_cygnus_afe_config(phydev); 99 if (rc) 100 return rc; 101 102 /* Advertise EEE */ 103 rc = bcm_phy_set_eee(phydev, true); 104 if (rc) 105 return rc; 106 107 /* Enable APD */ 108 return bcm_phy_enable_apd(phydev, false); 109 } 110 111 static int bcm_cygnus_resume(struct phy_device *phydev) 112 { 113 int rc; 114 115 genphy_resume(phydev); 116 117 /* Re-initialize the PHY to apply AFE work-arounds and 118 * configurations when coming out of suspend. 119 */ 120 rc = bcm_cygnus_config_init(phydev); 121 if (rc) 122 return rc; 123 124 /* restart auto negotiation with the new settings */ 125 return genphy_config_aneg(phydev); 126 } 127 128 static int bcm_omega_config_init(struct phy_device *phydev) 129 { 130 u8 count, rev; 131 int ret = 0; 132 133 rev = phydev->phy_id & ~phydev->drv->phy_id_mask; 134 135 pr_info_once("%s: %s PHY revision: 0x%02x\n", 136 phydev_name(phydev), phydev->drv->name, rev); 137 138 /* Dummy read to a register to workaround an issue upon reset where the 139 * internal inverter may not allow the first MDIO transaction to pass 140 * the MDIO management controller and make us return 0xffff for such 141 * reads. 142 */ 143 phy_read(phydev, MII_BMSR); 144 145 switch (rev) { 146 case 0x00: 147 ret = bcm_phy_28nm_a0b0_afe_config_init(phydev); 148 break; 149 default: 150 break; 151 } 152 153 if (ret) 154 return ret; 155 156 ret = bcm_phy_downshift_get(phydev, &count); 157 if (ret) 158 return ret; 159 160 /* Only enable EEE if Wirespeed/downshift is disabled */ 161 ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); 162 if (ret) 163 return ret; 164 165 return bcm_phy_enable_apd(phydev, true); 166 } 167 168 static int bcm_omega_resume(struct phy_device *phydev) 169 { 170 int ret; 171 172 /* Re-apply workarounds coming out suspend/resume */ 173 ret = bcm_omega_config_init(phydev); 174 if (ret) 175 return ret; 176 177 /* 28nm Gigabit PHYs come out of reset without any half-duplex 178 * or "hub" compliant advertised mode, fix that. This does not 179 * cause any problems with the PHY library since genphy_config_aneg() 180 * gracefully handles auto-negotiated and forced modes. 181 */ 182 return genphy_config_aneg(phydev); 183 } 184 185 static int bcm_omega_get_tunable(struct phy_device *phydev, 186 struct ethtool_tunable *tuna, void *data) 187 { 188 switch (tuna->id) { 189 case ETHTOOL_PHY_DOWNSHIFT: 190 return bcm_phy_downshift_get(phydev, (u8 *)data); 191 default: 192 return -EOPNOTSUPP; 193 } 194 } 195 196 static int bcm_omega_set_tunable(struct phy_device *phydev, 197 struct ethtool_tunable *tuna, 198 const void *data) 199 { 200 u8 count = *(u8 *)data; 201 int ret; 202 203 switch (tuna->id) { 204 case ETHTOOL_PHY_DOWNSHIFT: 205 ret = bcm_phy_downshift_set(phydev, count); 206 break; 207 default: 208 return -EOPNOTSUPP; 209 } 210 211 if (ret) 212 return ret; 213 214 /* Disable EEE advertisement since this prevents the PHY 215 * from successfully linking up, trigger auto-negotiation restart 216 * to let the MAC decide what to do. 217 */ 218 ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); 219 if (ret) 220 return ret; 221 222 return genphy_restart_aneg(phydev); 223 } 224 225 static void bcm_omega_get_phy_stats(struct phy_device *phydev, 226 struct ethtool_stats *stats, u64 *data) 227 { 228 struct bcm_omega_phy_priv *priv = phydev->priv; 229 230 bcm_phy_get_stats(phydev, priv->stats, stats, data); 231 } 232 233 static int bcm_omega_probe(struct phy_device *phydev) 234 { 235 struct bcm_omega_phy_priv *priv; 236 237 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 238 if (!priv) 239 return -ENOMEM; 240 241 phydev->priv = priv; 242 243 priv->stats = devm_kcalloc(&phydev->mdio.dev, 244 bcm_phy_get_sset_count(phydev), sizeof(u64), 245 GFP_KERNEL); 246 if (!priv->stats) 247 return -ENOMEM; 248 249 return 0; 250 } 251 252 static struct phy_driver bcm_cygnus_phy_driver[] = { 253 { 254 .phy_id = PHY_ID_BCM_CYGNUS, 255 .phy_id_mask = 0xfffffff0, 256 .name = "Broadcom Cygnus PHY", 257 /* PHY_GBIT_FEATURES */ 258 .config_init = bcm_cygnus_config_init, 259 .config_intr = bcm_phy_config_intr, 260 .handle_interrupt = bcm_phy_handle_interrupt, 261 .suspend = genphy_suspend, 262 .resume = bcm_cygnus_resume, 263 }, { 264 .phy_id = PHY_ID_BCM_OMEGA, 265 .phy_id_mask = 0xfffffff0, 266 .name = "Broadcom Omega Combo GPHY", 267 /* PHY_GBIT_FEATURES */ 268 .flags = PHY_IS_INTERNAL, 269 .config_init = bcm_omega_config_init, 270 .suspend = genphy_suspend, 271 .resume = bcm_omega_resume, 272 .get_tunable = bcm_omega_get_tunable, 273 .set_tunable = bcm_omega_set_tunable, 274 .get_sset_count = bcm_phy_get_sset_count, 275 .get_strings = bcm_phy_get_strings, 276 .get_stats = bcm_omega_get_phy_stats, 277 .probe = bcm_omega_probe, 278 } 279 }; 280 281 static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { 282 { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, 283 { PHY_ID_BCM_OMEGA, 0xfffffff0, }, 284 { } 285 }; 286 MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); 287 288 module_phy_driver(bcm_cygnus_phy_driver); 289 290 MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); 291 MODULE_LICENSE("GPL v2"); 292 MODULE_AUTHOR("Broadcom Corporation"); 293