1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018-2021 Beijing WangXun Technology Co., Ltd. 3 */ 4 5 #include "ngbe_phy_mvl.h" 6 7 #define MVL_PHY_RST_WAIT_PERIOD 5 8 9 s32 ngbe_read_phy_reg_mvl(struct ngbe_hw *hw, 10 u32 reg_addr, u32 device_type, u16 *phy_data) 11 { 12 mdi_reg_t reg; 13 mdi_reg_22_t reg22; 14 15 reg.device_type = device_type; 16 reg.addr = reg_addr; 17 18 if (hw->phy.media_type == ngbe_media_type_fiber) 19 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1); 20 else 21 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0); 22 23 ngbe_mdi_map_register(®, ®22); 24 25 ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); 26 27 return 0; 28 } 29 30 s32 ngbe_write_phy_reg_mvl(struct ngbe_hw *hw, 31 u32 reg_addr, u32 device_type, u16 phy_data) 32 { 33 mdi_reg_t reg; 34 mdi_reg_22_t reg22; 35 36 reg.device_type = device_type; 37 reg.addr = reg_addr; 38 39 if (hw->phy.media_type == ngbe_media_type_fiber) 40 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1); 41 else 42 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0); 43 44 ngbe_mdi_map_register(®, ®22); 45 46 ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); 47 48 return 0; 49 } 50 51 s32 ngbe_check_phy_mode_mvl(struct ngbe_hw *hw) 52 { 53 u8 value = 0; 54 u32 phy_mode = 0; 55 56 ngbe_flash_read_dword(hw, 0xFF010, &phy_mode); 57 value = (u8)(phy_mode >> (hw->bus.lan_id * 8)); 58 59 if (MVL_GEN_CTL_MODE(value) == MVL_GEN_CTL_MODE_COPPER) { 60 /* mode select to RGMII-to-copper */ 61 hw->phy.type = ngbe_phy_mvl; 62 hw->phy.media_type = ngbe_media_type_copper; 63 hw->mac.link_type = ngbe_link_copper; 64 } else if (MVL_GEN_CTL_MODE(value) == MVL_GEN_CTL_MODE_FIBER) { 65 /* mode select to RGMII-to-sfi */ 66 hw->phy.type = ngbe_phy_mvl_sfi; 67 hw->phy.media_type = ngbe_media_type_fiber; 68 hw->mac.link_type = ngbe_link_fiber; 69 } else { 70 DEBUGOUT("marvell 88E1512 mode %x is not supported.", value); 71 return NGBE_ERR_DEVICE_NOT_SUPPORTED; 72 } 73 74 return 0; 75 } 76 77 s32 ngbe_init_phy_mvl(struct ngbe_hw *hw) 78 { 79 s32 ret_val = 0; 80 u16 value = 0; 81 int i; 82 83 /* enable interrupts, only link status change and an done is allowed */ 84 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 2); 85 ngbe_read_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, &value); 86 value &= ~MVL_RGM_CTL2_TTC; 87 value |= MVL_RGM_CTL2_RTC; 88 ngbe_write_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, value); 89 90 hw->phy.write_reg(hw, MVL_CTRL, 0, MVL_CTRL_RESET); 91 for (i = 0; i < 15; i++) { 92 ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value); 93 if (value & MVL_CTRL_RESET) 94 msleep(1); 95 else 96 break; 97 } 98 99 if (i == 15) { 100 DEBUGOUT("phy reset exceeds maximum waiting period."); 101 return NGBE_ERR_TIMEOUT; 102 } 103 104 ret_val = hw->phy.reset_hw(hw); 105 if (ret_val) 106 return ret_val; 107 108 /* set LED2 to interrupt output and INTn active low */ 109 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3); 110 ngbe_read_phy_reg_mdi(hw, MVL_LEDTCR, 0, &value); 111 value |= MVL_LEDTCR_INTR_EN; 112 value &= ~(MVL_LEDTCR_INTR_POL); 113 ngbe_write_phy_reg_mdi(hw, MVL_LEDTCR, 0, value); 114 115 if (hw->phy.type == ngbe_phy_mvl_sfi) { 116 hw->phy.read_reg(hw, MVL_CTRL1, 0, &value); 117 value &= ~MVL_CTRL1_INTR_POL; 118 ngbe_write_phy_reg_mdi(hw, MVL_CTRL1, 0, value); 119 } 120 121 /* enable link status change and AN complete interrupts */ 122 value = MVL_INTR_EN_ANC | MVL_INTR_EN_LSC; 123 hw->phy.write_reg(hw, MVL_INTR_EN, 0, value); 124 125 hw->phy.set_phy_power(hw, false); 126 127 return ret_val; 128 } 129 130 s32 ngbe_setup_phy_link_mvl(struct ngbe_hw *hw, u32 speed, 131 bool autoneg_wait_to_complete) 132 { 133 u16 value_r4 = 0; 134 u16 value_r9 = 0; 135 u16 value; 136 137 UNREFERENCED_PARAMETER(autoneg_wait_to_complete); 138 139 if (hw->led_conf == 0xFFFF) { 140 /* LED control */ 141 ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3); 142 ngbe_read_phy_reg_mdi(hw, MVL_LEDFCR, 0, &value); 143 value &= ~(MVL_LEDFCR_CTL0 | MVL_LEDFCR_CTL1); 144 value |= MVL_LEDFCR_CTL0_CONF | MVL_LEDFCR_CTL1_CONF; 145 ngbe_write_phy_reg_mdi(hw, MVL_LEDFCR, 0, value); 146 ngbe_read_phy_reg_mdi(hw, MVL_LEDPCR, 0, &value); 147 value &= ~(MVL_LEDPCR_CTL0 | MVL_LEDPCR_CTL1); 148 value |= MVL_LEDPCR_CTL0_CONF | MVL_LEDPCR_CTL1_CONF; 149 ngbe_write_phy_reg_mdi(hw, MVL_LEDPCR, 0, value); 150 } 151 152 hw->phy.autoneg_advertised = 0; 153 154 if (hw->phy.type == ngbe_phy_mvl) { 155 if (!hw->mac.autoneg) { 156 switch (speed) { 157 case NGBE_LINK_SPEED_1GB_FULL: 158 value = MVL_CTRL_SPEED_SELECT1; 159 break; 160 case NGBE_LINK_SPEED_100M_FULL: 161 value = MVL_CTRL_SPEED_SELECT0; 162 break; 163 case NGBE_LINK_SPEED_10M_FULL: 164 value = 0; 165 break; 166 default: 167 value = MVL_CTRL_SPEED_SELECT0 | 168 MVL_CTRL_SPEED_SELECT1; 169 DEBUGOUT("unknown speed = 0x%x.", speed); 170 break; 171 } 172 /* duplex full */ 173 value |= MVL_CTRL_DUPLEX | MVL_CTRL_RESET; 174 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value); 175 176 goto skip_an; 177 } 178 if (speed & NGBE_LINK_SPEED_1GB_FULL) { 179 value_r9 |= MVL_PHY_1000BASET_FULL; 180 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; 181 } 182 183 if (speed & NGBE_LINK_SPEED_100M_FULL) { 184 value_r4 |= MVL_PHY_100BASET_FULL; 185 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL; 186 } 187 188 if (speed & NGBE_LINK_SPEED_10M_FULL) { 189 value_r4 |= MVL_PHY_10BASET_FULL; 190 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL; 191 } 192 193 hw->phy.read_reg(hw, MVL_ANA, 0, &value); 194 value &= ~(MVL_PHY_100BASET_FULL | 195 MVL_PHY_100BASET_HALF | 196 MVL_PHY_10BASET_FULL | 197 MVL_PHY_10BASET_HALF); 198 value_r4 |= value; 199 hw->phy.write_reg(hw, MVL_ANA, 0, value_r4); 200 201 hw->phy.read_reg(hw, MVL_PHY_1000BASET, 0, &value); 202 value &= ~(MVL_PHY_1000BASET_FULL | 203 MVL_PHY_1000BASET_HALF); 204 value_r9 |= value; 205 hw->phy.write_reg(hw, MVL_PHY_1000BASET, 0, value_r9); 206 207 value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE | 208 MVL_CTRL_RESET | MVL_CTRL_DUPLEX; 209 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value); 210 } else { 211 hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; 212 213 hw->phy.read_reg(hw, MVL_ANA, 0, &value); 214 value &= ~(MVL_PHY_1000BASEX_HALF | MVL_PHY_1000BASEX_FULL); 215 value |= MVL_PHY_1000BASEX_FULL; 216 hw->phy.write_reg(hw, MVL_ANA, 0, value); 217 218 if (hw->mac.autoneg) 219 value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE | 220 MVL_CTRL_RESET | MVL_CTRL_DUPLEX | 221 MVL_CTRL_SPEED_SELECT1; 222 else 223 value = MVL_CTRL_RESET | MVL_CTRL_DUPLEX | 224 MVL_CTRL_SPEED_SELECT1; 225 ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value); 226 } 227 228 skip_an: 229 hw->phy.set_phy_power(hw, true); 230 231 hw->phy.read_reg(hw, MVL_INTR, 0, &value); 232 233 return 0; 234 } 235 236 s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw) 237 { 238 u32 i; 239 u16 ctrl = 0; 240 s32 status = 0; 241 242 if (hw->phy.type != ngbe_phy_mvl && hw->phy.type != ngbe_phy_mvl_sfi) 243 return NGBE_ERR_PHY_TYPE; 244 245 /* select page 18 reg 20 */ 246 status = ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 18); 247 248 /* mode select to RGMII-to-copper or RGMII-to-sfi*/ 249 if (hw->phy.type == ngbe_phy_mvl) 250 ctrl = MVL_GEN_CTL_MODE_COPPER; 251 else 252 ctrl = MVL_GEN_CTL_MODE_FIBER; 253 status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl); 254 /* mode reset */ 255 ctrl |= MVL_GEN_CTL_RESET; 256 status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl); 257 258 for (i = 0; i < MVL_PHY_RST_WAIT_PERIOD; i++) { 259 status = ngbe_read_phy_reg_mdi(hw, MVL_GEN_CTL, 0, &ctrl); 260 if (!(ctrl & MVL_GEN_CTL_RESET)) 261 break; 262 msleep(1); 263 } 264 265 if (i == MVL_PHY_RST_WAIT_PERIOD) { 266 DEBUGOUT("PHY reset polling failed to complete."); 267 return NGBE_ERR_RESET_FAILED; 268 } 269 270 return status; 271 } 272 273 s32 ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit) 274 { 275 u16 value; 276 s32 status = 0; 277 278 if (hw->phy.type == ngbe_phy_mvl) { 279 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value); 280 value &= MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE; 281 *pause_bit = (u8)(value >> 10); 282 } else { 283 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value); 284 value &= MVL_FANA_PAUSE_MASK; 285 *pause_bit = (u8)(value >> 7); 286 } 287 288 return status; 289 } 290 291 s32 ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit) 292 { 293 u16 value; 294 s32 status = 0; 295 296 if (hw->phy.type == ngbe_phy_mvl) { 297 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value); 298 value &= MVL_CLPAR_ASM_PAUSE | MVL_CLPAR_PAUSE; 299 *pause_bit = (u8)(value >> 10); 300 } else { 301 status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value); 302 value &= MVL_FLPAR_PAUSE_MASK; 303 *pause_bit = (u8)(value >> 7); 304 } 305 306 return status; 307 } 308 309 s32 ngbe_set_phy_pause_adv_mvl(struct ngbe_hw *hw, u16 pause_bit) 310 { 311 u16 value; 312 s32 status = 0; 313 314 if (hw->phy.type == ngbe_phy_mvl) { 315 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value); 316 value &= ~(MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE); 317 } else { 318 status = hw->phy.read_reg(hw, MVL_ANA, 0, &value); 319 value &= ~MVL_FANA_PAUSE_MASK; 320 } 321 322 value |= pause_bit; 323 status = hw->phy.write_reg(hw, MVL_ANA, 0, value); 324 325 return status; 326 } 327 328 s32 ngbe_check_phy_link_mvl(struct ngbe_hw *hw, 329 u32 *speed, bool *link_up) 330 { 331 s32 status = 0; 332 u16 phy_link = 0; 333 u16 phy_speed = 0; 334 u16 phy_data = 0; 335 u16 insr = 0; 336 337 /* Initialize speed and link to default case */ 338 *link_up = false; 339 *speed = NGBE_LINK_SPEED_UNKNOWN; 340 341 hw->phy.read_reg(hw, MVL_INTR, 0, &insr); 342 343 /* 344 * Check current speed and link status of the PHY register. 345 * This is a vendor specific register and may have to 346 * be changed for other copper PHYs. 347 */ 348 status = hw->phy.read_reg(hw, MVL_PHYSR, 0, &phy_data); 349 phy_link = phy_data & MVL_PHYSR_LINK; 350 phy_speed = phy_data & MVL_PHYSR_SPEED_MASK; 351 352 if (phy_link == MVL_PHYSR_LINK) { 353 *link_up = true; 354 355 if (phy_speed == MVL_PHYSR_SPEED_1000M) 356 *speed = NGBE_LINK_SPEED_1GB_FULL; 357 else if (phy_speed == MVL_PHYSR_SPEED_100M) 358 *speed = NGBE_LINK_SPEED_100M_FULL; 359 else if (phy_speed == MVL_PHYSR_SPEED_10M) 360 *speed = NGBE_LINK_SPEED_10M_FULL; 361 } 362 363 return status; 364 } 365 366 s32 ngbe_set_phy_power_mvl(struct ngbe_hw *hw, bool on) 367 { 368 u16 value = 0; 369 370 hw->phy.read_reg(hw, MVL_CTRL, 0, &value); 371 if (on) 372 value &= ~MVL_CTRL_PWDN; 373 else 374 value |= MVL_CTRL_PWDN; 375 hw->phy.write_reg(hw, MVL_CTRL, 0, value); 376 377 return 0; 378 } 379