physfp.c (8892B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: physfp.c 8 * Purpose: Provide PHY driver support for the PHY device embedded in the 9 * copper SFP module. It is assumed that PHY device's register 10 * implementation conforms to IEEE 802.3 MII register specification. 11 * Device features implemented in the vendor specific registers generally 12 * will not be supported. 13 * This driver has tested with Broadcom 54810S copper SFP and 14 * Finisars 1000BASE-T SFP (FCxx-8520/1-3). 15 */ 16 17 #include <sal/types.h> 18 #include <sal/core/spl.h> 19 #include <shared/bsl.h> 20 #include <soc/drv.h> 21 #include <soc/debug.h> 22 #include <soc/error.h> 23 #include <soc/phyreg.h> 24 #include <soc/phy.h> 25 26 #include <soc/phy.h> 27 #include <soc/phy/phyctrl.h> 28 #include <soc/phy/drv.h> 29 30 #include "phyident.h" 31 #include "phyreg.h" 32 #include "phynull.h" 33 #include "phyfege.h" 34 35 36 #define PHY_BRCM_54810S_ID0 0x0362 37 #define PHY_BRCM_54810S_ID1 0x5d01 38 #define PHY_MRVL_88E1111_ID0 0x0141 39 #define PHY_MRVL_88E1111_ID1 0x0cc1 40 41 STATIC int 42 phy_copper_sfp_an_set(int unit, soc_port_t port, int an); 43 STATIC int 44 phy_copper_sfp_init(int unit, soc_port_t port); 45 STATIC int 46 phy_copper_sfp_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode); 47 STATIC int 48 phy_copper_sfp_enable_set(int unit, soc_port_t port, int enable); 49 STATIC int 50 phy_copper_sfp_speed_set(int unit, soc_port_t port, int speed); 51 52 53 /* 54 * Variable: 55 * phy_drv_copper_sfp 56 * Purpose: 57 * Phy driver callouts for a Gigabit phy (IEEE 802.3) 58 */ 59 phy_driver_t phy_copper_sfp_drv = { 60 "Copper SFP PHY Driver", 61 phy_copper_sfp_init, 62 phy_fe_ge_reset, 63 phy_fe_ge_link_get, 64 phy_copper_sfp_enable_set, 65 phy_fe_ge_enable_get, 66 phy_fe_ge_duplex_set, 67 phy_fe_ge_duplex_get, 68 phy_copper_sfp_speed_set, 69 phy_fe_ge_speed_get, 70 phy_fe_ge_master_set, 71 phy_fe_ge_master_get, 72 phy_copper_sfp_an_set, 73 phy_fe_ge_an_get, 74 phy_ge_adv_local_set, 75 phy_ge_adv_local_get, 76 phy_ge_adv_remote_get, 77 phy_fe_ge_lb_set, 78 phy_fe_ge_lb_get, 79 phy_ge_interface_set, 80 phy_ge_interface_get, 81 phy_copper_sfp_ability_get, 82 NULL, 83 NULL, 84 phy_null_mdix_set, 85 phy_null_mdix_get, 86 phy_null_mdix_status_get, 87 NULL, 88 NULL, 89 phy_fe_ge_medium_get, 90 NULL, /* phy_cable_diag */ 91 NULL, /* phy_link_change */ 92 NULL, /* .pd_control_set */ 93 NULL, /* .pd_control_get */ 94 NULL, /* .pd_reg_read */ 95 NULL, /* .pd_reg_write */ 96 NULL, /* .pd_reg_modify */ 97 NULL, /* .pd_notify */ 98 NULL /* .pd_probe */ 99 }; 100 101 102 /* 103 * Function: 104 * phy_finisar_sfp_init 105 * Purpose: 106 * Initialize the PHY (MII mode) to a known good state. 107 * Parameters: 108 * unit - StrataSwitch unit #. 109 * port - StrataSwitch port #. 110 * Returns: 111 * SOC_E_XXX 112 */ 113 114 STATIC int 115 phy_finisar_sfp_init(int unit, soc_port_t port) 116 { 117 phy_ctrl_t *pc; 118 pc = EXT_PHY_SW_STATE(unit, port); 119 120 LOG_INFO(BSL_LS_SOC_PHY, 121 (BSL_META_U(unit, 122 "phy_finisar_sfp_init : u=%d p=%d\n"), 123 unit, port)); 124 125 if (PHY_SGMII_AUTONEG_MODE(unit, port)) { 126 /* if it is in SGMII mode */ 127 SOC_IF_ERROR_RETURN 128 (WRITE_PHY_REG(unit,pc,0x1B,0x9084)); 129 SOC_IF_ERROR_RETURN 130 (WRITE_PHY_REG(unit,pc,0x9,0x0f00)); 131 SOC_IF_ERROR_RETURN 132 (WRITE_PHY_REG(unit,pc,0x0,0x8140)); 133 SOC_IF_ERROR_RETURN 134 (WRITE_PHY_REG(unit,pc,0x4,0x0de1)); 135 SOC_IF_ERROR_RETURN 136 (WRITE_PHY_REG(unit,pc,0x0,0x9140)); 137 } else { 138 SOC_IF_ERROR_RETURN 139 (WRITE_PHY_REG(unit,pc,0x1B,0x9088)); 140 SOC_IF_ERROR_RETURN 141 (WRITE_PHY_REG(unit,pc,0x9,0x0e00)); 142 SOC_IF_ERROR_RETURN 143 (WRITE_PHY_REG(unit,pc,0x4,0x0c01)); 144 SOC_IF_ERROR_RETURN 145 (WRITE_PHY_REG(unit,pc,0x0,0x8140)); 146 } 147 148 return(SOC_E_NONE); 149 } 150 151 /* 152 * Function: 153 * phy_copper_sfp_init 154 * Purpose: 155 * Initialize the PHY (MII mode) to a known good state. 156 * Parameters: 157 * unit - StrataSwitch unit #. 158 * port - StrataSwitch port #. 159 * Returns: 160 * SOC_E_XXX 161 162 * Notes: 163 * No synchronization performed at this level. 164 */ 165 166 STATIC int 167 phy_copper_sfp_init(int unit, soc_port_t port) 168 { 169 phy_ctrl_t *pc; 170 int rv; 171 172 pc = EXT_PHY_SW_STATE(unit, port); 173 174 if ((pc->phy_id0 == PHY_MRVL_88E1111_ID0) && 175 (pc->phy_id1 == PHY_MRVL_88E1111_ID1) ) { 176 rv = phy_finisar_sfp_init(unit,port); 177 } else if ((pc->phy_id0 == PHY_BRCM_54810S_ID0) && 178 (pc->phy_id1 == PHY_BRCM_54810S_ID1) ) { 179 rv = phy_ge_init(unit,port); 180 181 /* disable the broadreach mode for now */ 182 SOC_IF_ERROR_RETURN 183 (WRITE_PHY_REG(unit,pc,0x17,0xf90)); 184 SOC_IF_ERROR_RETURN 185 (WRITE_PHY_REG(unit,pc,0x15,0)); 186 187 } else { 188 rv = phy_ge_init(unit,port); 189 } 190 return rv; 191 } 192 193 /* 194 * Function: 195 * phy_copper_sfp_an_set 196 * Purpose: 197 * Enable or disabled auto-negotiation on the specified port. 198 * Parameters: 199 * unit - StrataSwitch unit #. 200 * port - StrataSwitch port #. 201 * an - Boolean, if true, auto-negotiation is enabled 202 * (and/or restarted). If false, autonegotiation is disabled. 203 * Returns: 204 * SOC_E_XXX 205 */ 206 207 STATIC int 208 phy_copper_sfp_an_set(int unit, soc_port_t port, int an) 209 { 210 phy_ctrl_t *pc; 211 int rv; 212 213 pc = EXT_PHY_SW_STATE(unit, port); 214 215 rv = phy_fe_ge_an_set(unit,port,an); 216 if ((pc->phy_id0 == PHY_MRVL_88E1111_ID0) && 217 (pc->phy_id1 == PHY_MRVL_88E1111_ID1) ) { 218 SOC_IF_ERROR_RETURN 219 (MODIFY_PHYFEGE_MII_CTRLr(unit, pc, MII_CTRL_RESET,MII_CTRL_RESET)); 220 } else if ((pc->phy_id0 == PHY_BRCM_54810S_ID0) && 221 (pc->phy_id1 == PHY_BRCM_54810S_ID1) ) { 222 } else { 223 } 224 return rv; 225 } 226 227 /* 228 * Function: 229 * phy_ge_ability_get 230 * Purpose: 231 * Get the abilities of the local gigabit phy. 232 * Parameters: 233 * unit - StrataSwitch unit #. 234 * port - StrataSwitch port #. 235 * mode - (OUT) Port mode mask indicating supported options/speeds. 236 * Returns: 237 * SOC_E_XXX 238 */ 239 240 STATIC int 241 phy_copper_sfp_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 242 { 243 uint16 mii_esr; 244 phy_ctrl_t *pc; 245 246 pc = EXT_PHY_SW_STATE(unit, port); 247 248 SOC_IF_ERROR_RETURN 249 (phy_fe_ability_get(unit, port, mode)); 250 251 *mode |= (SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | SOC_PM_GMII | SOC_PM_SGMII); 252 253 SOC_IF_ERROR_RETURN 254 (READ_PHYFEGE_MII_ESRr(unit, pc, &mii_esr)); 255 256 #if 0 257 258 259 if (mii_esr & MII_ESR_1000_T_FD) *mode |= SOC_PM_1000MB_FD; 260 if (mii_esr & MII_ESR_1000_T_HD) *mode |= SOC_PM_1000MB_HD; 261 if (mii_esr & MII_ESR_1000_X_FD) *mode |= SOC_PM_1000MB_FD; 262 if (mii_esr & MII_ESR_1000_X_HD) *mode |= SOC_PM_1000MB_HD; 263 #else 264 *mode |= SOC_PM_1000MB_HD | SOC_PM_1000MB_FD; 265 #endif 266 267 return(SOC_E_NONE); 268 } 269 270 /* 271 * Function: 272 * phy_copper_sfp_enable_set 273 * Purpose: 274 * Enable or disable the physical interface. 275 * Parameters: 276 * unit - StrataSwitch unit #. 277 * port - StrataSwitch port #. 278 * enable - Boolean, true = enable PHY, false = disable. 279 * Returns: 280 * SOC_E_XXX 281 */ 282 283 STATIC int 284 phy_copper_sfp_enable_set(int unit, soc_port_t port, int enable) 285 { 286 int value; 287 phy_ctrl_t *pc; 288 289 pc = EXT_PHY_SW_STATE(unit, port); 290 if (enable) { 291 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE); 292 } else { 293 PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE); 294 } 295 296 value = enable? 0: MII_CTRL_PD; 297 SOC_IF_ERROR_RETURN 298 (MODIFY_PHYFEGE_MII_CTRLr(unit, pc,value,MII_CTRL_PD)); 299 300 LOG_INFO(BSL_LS_SOC_PHY, 301 (BSL_META_U(unit, 302 "phy_copper_sfp_enable_set: u=%d p=%d value=%d\n"), 303 unit, port,value)); 304 return (SOC_E_NONE); 305 } 306 307 /* 308 * Function: 309 * phy_copper_sfp_speed_set 310 * Purpose: 311 * Set the current operating speed (forced). 312 * Parameters: 313 * unit - StrataSwitch unit #. 314 * port - StrataSwitch port #. 315 * duplex - (OUT) Boolean, true indicates full duplex, false 316 * indicates half. 317 * Returns: 318 * SOC_E_XXX 319 */ 320 321 STATIC int 322 phy_copper_sfp_speed_set(int unit, soc_port_t port, int speed) 323 { 324 phy_ctrl_t *pc; 325 326 pc = EXT_PHY_SW_STATE(unit, port); 327 328 SOC_IF_ERROR_RETURN 329 (phy_fe_ge_speed_set(unit,port,speed)); 330 331 if ((pc->phy_id0 == PHY_MRVL_88E1111_ID0) && 332 (pc->phy_id1 == PHY_MRVL_88E1111_ID1) ) { 333 SOC_IF_ERROR_RETURN 334 (MODIFY_PHYFEGE_MII_CTRLr(unit, pc, MII_CTRL_RESET,MII_CTRL_RESET)); 335 } 336 return SOC_E_NONE; 337 }