1517904deSPeter Grehan /*- 2517904deSPeter Grehan * Copyright 2021 Intel Corp 3517904deSPeter Grehan * Copyright 2021 Rubicon Communications, LLC (Netgate) 4517904deSPeter Grehan * SPDX-License-Identifier: BSD-3-Clause 5517904deSPeter Grehan */ 6517904deSPeter Grehan 7517904deSPeter Grehan #include <sys/cdefs.h> 8517904deSPeter Grehan __FBSDID("$FreeBSD$"); 9517904deSPeter Grehan 10517904deSPeter Grehan #include "igc_api.h" 11517904deSPeter Grehan 12517904deSPeter Grehan static s32 igc_wait_autoneg(struct igc_hw *hw); 13517904deSPeter Grehan 14517904deSPeter Grehan /** 15517904deSPeter Grehan * igc_init_phy_ops_generic - Initialize PHY function pointers 16517904deSPeter Grehan * @hw: pointer to the HW structure 17517904deSPeter Grehan * 18517904deSPeter Grehan * Setups up the function pointers to no-op functions 19517904deSPeter Grehan **/ 20517904deSPeter Grehan void igc_init_phy_ops_generic(struct igc_hw *hw) 21517904deSPeter Grehan { 22517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 23517904deSPeter Grehan DEBUGFUNC("igc_init_phy_ops_generic"); 24517904deSPeter Grehan 25517904deSPeter Grehan /* Initialize function pointers */ 26517904deSPeter Grehan phy->ops.init_params = igc_null_ops_generic; 27517904deSPeter Grehan phy->ops.acquire = igc_null_ops_generic; 28517904deSPeter Grehan phy->ops.check_reset_block = igc_null_ops_generic; 29517904deSPeter Grehan phy->ops.force_speed_duplex = igc_null_ops_generic; 30517904deSPeter Grehan phy->ops.get_info = igc_null_ops_generic; 31517904deSPeter Grehan phy->ops.set_page = igc_null_set_page; 32517904deSPeter Grehan phy->ops.read_reg = igc_null_read_reg; 33517904deSPeter Grehan phy->ops.read_reg_locked = igc_null_read_reg; 34517904deSPeter Grehan phy->ops.read_reg_page = igc_null_read_reg; 35517904deSPeter Grehan phy->ops.release = igc_null_phy_generic; 36517904deSPeter Grehan phy->ops.reset = igc_null_ops_generic; 37517904deSPeter Grehan phy->ops.set_d0_lplu_state = igc_null_lplu_state; 38517904deSPeter Grehan phy->ops.set_d3_lplu_state = igc_null_lplu_state; 39517904deSPeter Grehan phy->ops.write_reg = igc_null_write_reg; 40517904deSPeter Grehan phy->ops.write_reg_locked = igc_null_write_reg; 41517904deSPeter Grehan phy->ops.write_reg_page = igc_null_write_reg; 42517904deSPeter Grehan phy->ops.power_up = igc_null_phy_generic; 43517904deSPeter Grehan phy->ops.power_down = igc_null_phy_generic; 44517904deSPeter Grehan } 45517904deSPeter Grehan 46517904deSPeter Grehan /** 47517904deSPeter Grehan * igc_null_set_page - No-op function, return 0 48517904deSPeter Grehan * @hw: pointer to the HW structure 49517904deSPeter Grehan * @data: dummy variable 50517904deSPeter Grehan **/ 51517904deSPeter Grehan s32 igc_null_set_page(struct igc_hw IGC_UNUSEDARG *hw, 52517904deSPeter Grehan u16 IGC_UNUSEDARG data) 53517904deSPeter Grehan { 54517904deSPeter Grehan DEBUGFUNC("igc_null_set_page"); 55517904deSPeter Grehan return IGC_SUCCESS; 56517904deSPeter Grehan } 57517904deSPeter Grehan 58517904deSPeter Grehan /** 59517904deSPeter Grehan * igc_null_read_reg - No-op function, return 0 60517904deSPeter Grehan * @hw: pointer to the HW structure 61517904deSPeter Grehan * @offset: dummy variable 62517904deSPeter Grehan * @data: dummy variable 63517904deSPeter Grehan **/ 64517904deSPeter Grehan s32 igc_null_read_reg(struct igc_hw IGC_UNUSEDARG *hw, 65517904deSPeter Grehan u32 IGC_UNUSEDARG offset, u16 IGC_UNUSEDARG *data) 66517904deSPeter Grehan { 67517904deSPeter Grehan DEBUGFUNC("igc_null_read_reg"); 68517904deSPeter Grehan return IGC_SUCCESS; 69517904deSPeter Grehan } 70517904deSPeter Grehan 71517904deSPeter Grehan /** 72517904deSPeter Grehan * igc_null_phy_generic - No-op function, return void 73517904deSPeter Grehan * @hw: pointer to the HW structure 74517904deSPeter Grehan **/ 75517904deSPeter Grehan void igc_null_phy_generic(struct igc_hw IGC_UNUSEDARG *hw) 76517904deSPeter Grehan { 77517904deSPeter Grehan DEBUGFUNC("igc_null_phy_generic"); 78517904deSPeter Grehan return; 79517904deSPeter Grehan } 80517904deSPeter Grehan 81517904deSPeter Grehan /** 82517904deSPeter Grehan * igc_null_lplu_state - No-op function, return 0 83517904deSPeter Grehan * @hw: pointer to the HW structure 84517904deSPeter Grehan * @active: dummy variable 85517904deSPeter Grehan **/ 86517904deSPeter Grehan s32 igc_null_lplu_state(struct igc_hw IGC_UNUSEDARG *hw, 87517904deSPeter Grehan bool IGC_UNUSEDARG active) 88517904deSPeter Grehan { 89517904deSPeter Grehan DEBUGFUNC("igc_null_lplu_state"); 90517904deSPeter Grehan return IGC_SUCCESS; 91517904deSPeter Grehan } 92517904deSPeter Grehan 93517904deSPeter Grehan /** 94517904deSPeter Grehan * igc_null_write_reg - No-op function, return 0 95517904deSPeter Grehan * @hw: pointer to the HW structure 96517904deSPeter Grehan * @offset: dummy variable 97517904deSPeter Grehan * @data: dummy variable 98517904deSPeter Grehan **/ 99517904deSPeter Grehan s32 igc_null_write_reg(struct igc_hw IGC_UNUSEDARG *hw, 100517904deSPeter Grehan u32 IGC_UNUSEDARG offset, u16 IGC_UNUSEDARG data) 101517904deSPeter Grehan { 102517904deSPeter Grehan DEBUGFUNC("igc_null_write_reg"); 103517904deSPeter Grehan return IGC_SUCCESS; 104517904deSPeter Grehan } 105517904deSPeter Grehan 106517904deSPeter Grehan /** 107517904deSPeter Grehan * igc_check_reset_block_generic - Check if PHY reset is blocked 108517904deSPeter Grehan * @hw: pointer to the HW structure 109517904deSPeter Grehan * 110517904deSPeter Grehan * Read the PHY management control register and check whether a PHY reset 111517904deSPeter Grehan * is blocked. If a reset is not blocked return IGC_SUCCESS, otherwise 112517904deSPeter Grehan * return IGC_BLK_PHY_RESET (12). 113517904deSPeter Grehan **/ 114517904deSPeter Grehan s32 igc_check_reset_block_generic(struct igc_hw *hw) 115517904deSPeter Grehan { 116517904deSPeter Grehan u32 manc; 117517904deSPeter Grehan 118517904deSPeter Grehan DEBUGFUNC("igc_check_reset_block"); 119517904deSPeter Grehan 120517904deSPeter Grehan manc = IGC_READ_REG(hw, IGC_MANC); 121517904deSPeter Grehan 122517904deSPeter Grehan return (manc & IGC_MANC_BLK_PHY_RST_ON_IDE) ? 123517904deSPeter Grehan IGC_BLK_PHY_RESET : IGC_SUCCESS; 124517904deSPeter Grehan } 125517904deSPeter Grehan 126517904deSPeter Grehan /** 127517904deSPeter Grehan * igc_get_phy_id - Retrieve the PHY ID and revision 128517904deSPeter Grehan * @hw: pointer to the HW structure 129517904deSPeter Grehan * 130517904deSPeter Grehan * Reads the PHY registers and stores the PHY ID and possibly the PHY 131517904deSPeter Grehan * revision in the hardware structure. 132517904deSPeter Grehan **/ 133517904deSPeter Grehan s32 igc_get_phy_id(struct igc_hw *hw) 134517904deSPeter Grehan { 135517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 136517904deSPeter Grehan s32 ret_val = IGC_SUCCESS; 137517904deSPeter Grehan u16 phy_id; 138517904deSPeter Grehan 139517904deSPeter Grehan DEBUGFUNC("igc_get_phy_id"); 140517904deSPeter Grehan 141517904deSPeter Grehan if (!phy->ops.read_reg) 142517904deSPeter Grehan return IGC_SUCCESS; 143517904deSPeter Grehan 144517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); 145517904deSPeter Grehan if (ret_val) 146517904deSPeter Grehan return ret_val; 147517904deSPeter Grehan 148517904deSPeter Grehan phy->id = (u32)(phy_id << 16); 149561cd74bSPeter Grehan usec_delay(200); 150517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); 151517904deSPeter Grehan if (ret_val) 152517904deSPeter Grehan return ret_val; 153517904deSPeter Grehan 154517904deSPeter Grehan phy->id |= (u32)(phy_id & PHY_REVISION_MASK); 155517904deSPeter Grehan phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); 156517904deSPeter Grehan 157517904deSPeter Grehan return IGC_SUCCESS; 158517904deSPeter Grehan } 159517904deSPeter Grehan 160517904deSPeter Grehan /** 161517904deSPeter Grehan * igc_read_phy_reg_mdic - Read MDI control register 162517904deSPeter Grehan * @hw: pointer to the HW structure 163517904deSPeter Grehan * @offset: register offset to be read 164517904deSPeter Grehan * @data: pointer to the read data 165517904deSPeter Grehan * 166517904deSPeter Grehan * Reads the MDI control register in the PHY at offset and stores the 167517904deSPeter Grehan * information read to data. 168517904deSPeter Grehan **/ 169517904deSPeter Grehan s32 igc_read_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 *data) 170517904deSPeter Grehan { 171517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 172517904deSPeter Grehan u32 i, mdic = 0; 173517904deSPeter Grehan 174517904deSPeter Grehan DEBUGFUNC("igc_read_phy_reg_mdic"); 175517904deSPeter Grehan 176517904deSPeter Grehan if (offset > MAX_PHY_REG_ADDRESS) { 177517904deSPeter Grehan DEBUGOUT1("PHY Address %d is out of range\n", offset); 178517904deSPeter Grehan return -IGC_ERR_PARAM; 179517904deSPeter Grehan } 180517904deSPeter Grehan 181517904deSPeter Grehan /* Set up Op-code, Phy Address, and register offset in the MDI 182517904deSPeter Grehan * Control register. The MAC will take care of interfacing with the 183517904deSPeter Grehan * PHY to retrieve the desired data. 184517904deSPeter Grehan */ 185517904deSPeter Grehan mdic = ((offset << IGC_MDIC_REG_SHIFT) | 186517904deSPeter Grehan (phy->addr << IGC_MDIC_PHY_SHIFT) | 187517904deSPeter Grehan (IGC_MDIC_OP_READ)); 188517904deSPeter Grehan 189517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_MDIC, mdic); 190517904deSPeter Grehan 191517904deSPeter Grehan /* Poll the ready bit to see if the MDI read completed 192517904deSPeter Grehan * Increasing the time out as testing showed failures with 193517904deSPeter Grehan * the lower time out 194517904deSPeter Grehan */ 195517904deSPeter Grehan for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) { 196517904deSPeter Grehan usec_delay_irq(50); 197517904deSPeter Grehan mdic = IGC_READ_REG(hw, IGC_MDIC); 198517904deSPeter Grehan if (mdic & IGC_MDIC_READY) 199517904deSPeter Grehan break; 200517904deSPeter Grehan } 201517904deSPeter Grehan if (!(mdic & IGC_MDIC_READY)) { 202517904deSPeter Grehan DEBUGOUT("MDI Read did not complete\n"); 203517904deSPeter Grehan return -IGC_ERR_PHY; 204517904deSPeter Grehan } 205517904deSPeter Grehan if (mdic & IGC_MDIC_ERROR) { 206517904deSPeter Grehan DEBUGOUT("MDI Error\n"); 207517904deSPeter Grehan return -IGC_ERR_PHY; 208517904deSPeter Grehan } 209517904deSPeter Grehan if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset) { 210517904deSPeter Grehan DEBUGOUT2("MDI Read offset error - requested %d, returned %d\n", 211517904deSPeter Grehan offset, 212517904deSPeter Grehan (mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT); 213517904deSPeter Grehan return -IGC_ERR_PHY; 214517904deSPeter Grehan } 215517904deSPeter Grehan *data = (u16) mdic; 216517904deSPeter Grehan 217517904deSPeter Grehan return IGC_SUCCESS; 218517904deSPeter Grehan } 219517904deSPeter Grehan 220517904deSPeter Grehan /** 221517904deSPeter Grehan * igc_write_phy_reg_mdic - Write MDI control register 222517904deSPeter Grehan * @hw: pointer to the HW structure 223517904deSPeter Grehan * @offset: register offset to write to 224517904deSPeter Grehan * @data: data to write to register at offset 225517904deSPeter Grehan * 226517904deSPeter Grehan * Writes data to MDI control register in the PHY at offset. 227517904deSPeter Grehan **/ 228517904deSPeter Grehan s32 igc_write_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 data) 229517904deSPeter Grehan { 230517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 231517904deSPeter Grehan u32 i, mdic = 0; 232517904deSPeter Grehan 233517904deSPeter Grehan DEBUGFUNC("igc_write_phy_reg_mdic"); 234517904deSPeter Grehan 235517904deSPeter Grehan if (offset > MAX_PHY_REG_ADDRESS) { 236517904deSPeter Grehan DEBUGOUT1("PHY Address %d is out of range\n", offset); 237517904deSPeter Grehan return -IGC_ERR_PARAM; 238517904deSPeter Grehan } 239517904deSPeter Grehan 240517904deSPeter Grehan /* Set up Op-code, Phy Address, and register offset in the MDI 241517904deSPeter Grehan * Control register. The MAC will take care of interfacing with the 242517904deSPeter Grehan * PHY to retrieve the desired data. 243517904deSPeter Grehan */ 244517904deSPeter Grehan mdic = (((u32)data) | 245517904deSPeter Grehan (offset << IGC_MDIC_REG_SHIFT) | 246517904deSPeter Grehan (phy->addr << IGC_MDIC_PHY_SHIFT) | 247517904deSPeter Grehan (IGC_MDIC_OP_WRITE)); 248517904deSPeter Grehan 249517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_MDIC, mdic); 250517904deSPeter Grehan 251517904deSPeter Grehan /* Poll the ready bit to see if the MDI read completed 252517904deSPeter Grehan * Increasing the time out as testing showed failures with 253517904deSPeter Grehan * the lower time out 254517904deSPeter Grehan */ 255517904deSPeter Grehan for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) { 256517904deSPeter Grehan usec_delay_irq(50); 257517904deSPeter Grehan mdic = IGC_READ_REG(hw, IGC_MDIC); 258517904deSPeter Grehan if (mdic & IGC_MDIC_READY) 259517904deSPeter Grehan break; 260517904deSPeter Grehan } 261517904deSPeter Grehan if (!(mdic & IGC_MDIC_READY)) { 262517904deSPeter Grehan DEBUGOUT("MDI Write did not complete\n"); 263517904deSPeter Grehan return -IGC_ERR_PHY; 264517904deSPeter Grehan } 265517904deSPeter Grehan if (mdic & IGC_MDIC_ERROR) { 266517904deSPeter Grehan DEBUGOUT("MDI Error\n"); 267517904deSPeter Grehan return -IGC_ERR_PHY; 268517904deSPeter Grehan } 269517904deSPeter Grehan if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset) { 270517904deSPeter Grehan DEBUGOUT2("MDI Write offset error - requested %d, returned %d\n", 271517904deSPeter Grehan offset, 272517904deSPeter Grehan (mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT); 273517904deSPeter Grehan return -IGC_ERR_PHY; 274517904deSPeter Grehan } 275517904deSPeter Grehan 276517904deSPeter Grehan return IGC_SUCCESS; 277517904deSPeter Grehan } 278517904deSPeter Grehan 279517904deSPeter Grehan /** 280517904deSPeter Grehan * igc_phy_setup_autoneg - Configure PHY for auto-negotiation 281517904deSPeter Grehan * @hw: pointer to the HW structure 282517904deSPeter Grehan * 283517904deSPeter Grehan * Reads the MII auto-neg advertisement register and/or the 1000T control 284517904deSPeter Grehan * register and if the PHY is already setup for auto-negotiation, then 285517904deSPeter Grehan * return successful. Otherwise, setup advertisement and flow control to 286517904deSPeter Grehan * the appropriate values for the wanted auto-negotiation. 287517904deSPeter Grehan **/ 288517904deSPeter Grehan static s32 igc_phy_setup_autoneg(struct igc_hw *hw) 289517904deSPeter Grehan { 290517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 291517904deSPeter Grehan s32 ret_val; 292517904deSPeter Grehan u16 mii_autoneg_adv_reg; 293517904deSPeter Grehan u16 mii_1000t_ctrl_reg = 0; 294517904deSPeter Grehan u16 aneg_multigbt_an_ctrl = 0; 295517904deSPeter Grehan 296517904deSPeter Grehan DEBUGFUNC("igc_phy_setup_autoneg"); 297517904deSPeter Grehan 298517904deSPeter Grehan phy->autoneg_advertised &= phy->autoneg_mask; 299517904deSPeter Grehan 300517904deSPeter Grehan /* Read the MII Auto-Neg Advertisement Register (Address 4). */ 301517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); 302517904deSPeter Grehan if (ret_val) 303517904deSPeter Grehan return ret_val; 304517904deSPeter Grehan 305517904deSPeter Grehan if (phy->autoneg_mask & ADVERTISE_1000_FULL) { 306517904deSPeter Grehan /* Read the MII 1000Base-T Control Register (Address 9). */ 307517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, 308517904deSPeter Grehan &mii_1000t_ctrl_reg); 309517904deSPeter Grehan if (ret_val) 310517904deSPeter Grehan return ret_val; 311517904deSPeter Grehan } 312517904deSPeter Grehan 313*29d7f1ffSMah Yock Gen if (phy->autoneg_mask & ADVERTISE_2500_FULL) { 314517904deSPeter Grehan /* Read the MULTI GBT AN Control Register - reg 7.32 */ 315517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK << 316517904deSPeter Grehan MMD_DEVADDR_SHIFT) | 317517904deSPeter Grehan ANEG_MULTIGBT_AN_CTRL, 318517904deSPeter Grehan &aneg_multigbt_an_ctrl); 319517904deSPeter Grehan 320517904deSPeter Grehan if (ret_val) 321517904deSPeter Grehan return ret_val; 322517904deSPeter Grehan } 323517904deSPeter Grehan 324517904deSPeter Grehan /* Need to parse both autoneg_advertised and fc and set up 325517904deSPeter Grehan * the appropriate PHY registers. First we will parse for 326517904deSPeter Grehan * autoneg_advertised software override. Since we can advertise 327517904deSPeter Grehan * a plethora of combinations, we need to check each bit 328517904deSPeter Grehan * individually. 329517904deSPeter Grehan */ 330517904deSPeter Grehan 331517904deSPeter Grehan /* First we clear all the 10/100 mb speed bits in the Auto-Neg 332517904deSPeter Grehan * Advertisement Register (Address 4) and the 1000 mb speed bits in 333517904deSPeter Grehan * the 1000Base-T Control Register (Address 9). 334517904deSPeter Grehan */ 335517904deSPeter Grehan mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | 336517904deSPeter Grehan NWAY_AR_100TX_HD_CAPS | 337517904deSPeter Grehan NWAY_AR_10T_FD_CAPS | 338517904deSPeter Grehan NWAY_AR_10T_HD_CAPS); 339517904deSPeter Grehan mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); 340517904deSPeter Grehan 341517904deSPeter Grehan DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised); 342517904deSPeter Grehan 343517904deSPeter Grehan /* Do we want to advertise 10 Mb Half Duplex? */ 344517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_10_HALF) { 345517904deSPeter Grehan DEBUGOUT("Advertise 10mb Half duplex\n"); 346517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; 347517904deSPeter Grehan } 348517904deSPeter Grehan 349517904deSPeter Grehan /* Do we want to advertise 10 Mb Full Duplex? */ 350517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_10_FULL) { 351517904deSPeter Grehan DEBUGOUT("Advertise 10mb Full duplex\n"); 352517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; 353517904deSPeter Grehan } 354517904deSPeter Grehan 355517904deSPeter Grehan /* Do we want to advertise 100 Mb Half Duplex? */ 356517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_100_HALF) { 357517904deSPeter Grehan DEBUGOUT("Advertise 100mb Half duplex\n"); 358517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; 359517904deSPeter Grehan } 360517904deSPeter Grehan 361517904deSPeter Grehan /* Do we want to advertise 100 Mb Full Duplex? */ 362517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_100_FULL) { 363517904deSPeter Grehan DEBUGOUT("Advertise 100mb Full duplex\n"); 364517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; 365517904deSPeter Grehan } 366517904deSPeter Grehan 367517904deSPeter Grehan /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ 368517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_1000_HALF) 369517904deSPeter Grehan DEBUGOUT("Advertise 1000mb Half duplex request denied!\n"); 370517904deSPeter Grehan 371517904deSPeter Grehan /* Do we want to advertise 1000 Mb Full Duplex? */ 372517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { 373517904deSPeter Grehan DEBUGOUT("Advertise 1000mb Full duplex\n"); 374517904deSPeter Grehan mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; 375517904deSPeter Grehan } 376517904deSPeter Grehan 377517904deSPeter Grehan /* We do not allow the Phy to advertise 2500 Mb Half Duplex */ 378517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_2500_HALF) 379517904deSPeter Grehan DEBUGOUT("Advertise 2500mb Half duplex request denied!\n"); 380517904deSPeter Grehan 381517904deSPeter Grehan /* Do we want to advertise 2500 Mb Full Duplex? */ 382517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_2500_FULL) { 383517904deSPeter Grehan DEBUGOUT("Advertise 2500mb Full duplex\n"); 384517904deSPeter Grehan aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS; 385517904deSPeter Grehan } else { 386517904deSPeter Grehan aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS; 387517904deSPeter Grehan } 388517904deSPeter Grehan 389517904deSPeter Grehan /* Check for a software override of the flow control settings, and 390517904deSPeter Grehan * setup the PHY advertisement registers accordingly. If 391517904deSPeter Grehan * auto-negotiation is enabled, then software will have to set the 392517904deSPeter Grehan * "PAUSE" bits to the correct value in the Auto-Negotiation 393517904deSPeter Grehan * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- 394517904deSPeter Grehan * negotiation. 395517904deSPeter Grehan * 396517904deSPeter Grehan * The possible values of the "fc" parameter are: 397517904deSPeter Grehan * 0: Flow control is completely disabled 398517904deSPeter Grehan * 1: Rx flow control is enabled (we can receive pause frames 399517904deSPeter Grehan * but not send pause frames). 400517904deSPeter Grehan * 2: Tx flow control is enabled (we can send pause frames 401517904deSPeter Grehan * but we do not support receiving pause frames). 402517904deSPeter Grehan * 3: Both Rx and Tx flow control (symmetric) are enabled. 403517904deSPeter Grehan * other: No software override. The flow control configuration 404517904deSPeter Grehan * in the EEPROM is used. 405517904deSPeter Grehan */ 406517904deSPeter Grehan switch (hw->fc.current_mode) { 407517904deSPeter Grehan case igc_fc_none: 408517904deSPeter Grehan /* Flow control (Rx & Tx) is completely disabled by a 409517904deSPeter Grehan * software over-ride. 410517904deSPeter Grehan */ 411517904deSPeter Grehan mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 412517904deSPeter Grehan break; 413517904deSPeter Grehan case igc_fc_rx_pause: 414517904deSPeter Grehan /* Rx Flow control is enabled, and Tx Flow control is 415517904deSPeter Grehan * disabled, by a software over-ride. 416517904deSPeter Grehan * 417517904deSPeter Grehan * Since there really isn't a way to advertise that we are 418517904deSPeter Grehan * capable of Rx Pause ONLY, we will advertise that we 419517904deSPeter Grehan * support both symmetric and asymmetric Rx PAUSE. Later 420517904deSPeter Grehan * (in igc_config_fc_after_link_up) we will disable the 421517904deSPeter Grehan * hw's ability to send PAUSE frames. 422517904deSPeter Grehan */ 423517904deSPeter Grehan mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 424517904deSPeter Grehan break; 425517904deSPeter Grehan case igc_fc_tx_pause: 426517904deSPeter Grehan /* Tx Flow control is enabled, and Rx Flow control is 427517904deSPeter Grehan * disabled, by a software over-ride. 428517904deSPeter Grehan */ 429517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; 430517904deSPeter Grehan mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; 431517904deSPeter Grehan break; 432517904deSPeter Grehan case igc_fc_full: 433517904deSPeter Grehan /* Flow control (both Rx and Tx) is enabled by a software 434517904deSPeter Grehan * over-ride. 435517904deSPeter Grehan */ 436517904deSPeter Grehan mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 437517904deSPeter Grehan break; 438517904deSPeter Grehan default: 439517904deSPeter Grehan DEBUGOUT("Flow control param set incorrectly\n"); 440517904deSPeter Grehan return -IGC_ERR_CONFIG; 441517904deSPeter Grehan } 442517904deSPeter Grehan 443517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); 444517904deSPeter Grehan if (ret_val) 445517904deSPeter Grehan return ret_val; 446517904deSPeter Grehan 447517904deSPeter Grehan DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); 448517904deSPeter Grehan 449517904deSPeter Grehan if (phy->autoneg_mask & ADVERTISE_1000_FULL) 450517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, 451517904deSPeter Grehan mii_1000t_ctrl_reg); 452517904deSPeter Grehan 453*29d7f1ffSMah Yock Gen if (phy->autoneg_mask & ADVERTISE_2500_FULL) 454517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 455517904deSPeter Grehan (STANDARD_AN_REG_MASK << 456517904deSPeter Grehan MMD_DEVADDR_SHIFT) | 457517904deSPeter Grehan ANEG_MULTIGBT_AN_CTRL, 458517904deSPeter Grehan aneg_multigbt_an_ctrl); 459517904deSPeter Grehan 460517904deSPeter Grehan return ret_val; 461517904deSPeter Grehan } 462517904deSPeter Grehan 463517904deSPeter Grehan /** 464517904deSPeter Grehan * igc_copper_link_autoneg - Setup/Enable autoneg for copper link 465517904deSPeter Grehan * @hw: pointer to the HW structure 466517904deSPeter Grehan * 467517904deSPeter Grehan * Performs initial bounds checking on autoneg advertisement parameter, then 468517904deSPeter Grehan * configure to advertise the full capability. Setup the PHY to autoneg 469517904deSPeter Grehan * and restart the negotiation process between the link partner. If 470517904deSPeter Grehan * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. 471517904deSPeter Grehan **/ 472517904deSPeter Grehan static s32 igc_copper_link_autoneg(struct igc_hw *hw) 473517904deSPeter Grehan { 474517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 475517904deSPeter Grehan s32 ret_val; 476517904deSPeter Grehan u16 phy_ctrl; 477517904deSPeter Grehan 478517904deSPeter Grehan DEBUGFUNC("igc_copper_link_autoneg"); 479517904deSPeter Grehan 480517904deSPeter Grehan /* Perform some bounds checking on the autoneg advertisement 481517904deSPeter Grehan * parameter. 482517904deSPeter Grehan */ 483517904deSPeter Grehan phy->autoneg_advertised &= phy->autoneg_mask; 484517904deSPeter Grehan 485517904deSPeter Grehan /* If autoneg_advertised is zero, we assume it was not defaulted 486517904deSPeter Grehan * by the calling code so we set to advertise full capability. 487517904deSPeter Grehan */ 488517904deSPeter Grehan if (!phy->autoneg_advertised) 489517904deSPeter Grehan phy->autoneg_advertised = phy->autoneg_mask; 490517904deSPeter Grehan 491517904deSPeter Grehan DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); 492517904deSPeter Grehan ret_val = igc_phy_setup_autoneg(hw); 493517904deSPeter Grehan if (ret_val) { 494517904deSPeter Grehan DEBUGOUT("Error Setting up Auto-Negotiation\n"); 495517904deSPeter Grehan return ret_val; 496517904deSPeter Grehan } 497517904deSPeter Grehan DEBUGOUT("Restarting Auto-Neg\n"); 498517904deSPeter Grehan 499517904deSPeter Grehan /* Restart auto-negotiation by setting the Auto Neg Enable bit and 500517904deSPeter Grehan * the Auto Neg Restart bit in the PHY control register. 501517904deSPeter Grehan */ 502517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); 503517904deSPeter Grehan if (ret_val) 504517904deSPeter Grehan return ret_val; 505517904deSPeter Grehan 506517904deSPeter Grehan phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); 507517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); 508517904deSPeter Grehan if (ret_val) 509517904deSPeter Grehan return ret_val; 510517904deSPeter Grehan 511517904deSPeter Grehan /* Does the user want to wait for Auto-Neg to complete here, or 512517904deSPeter Grehan * check at a later time (for example, callback routine). 513517904deSPeter Grehan */ 514517904deSPeter Grehan if (phy->autoneg_wait_to_complete) { 515517904deSPeter Grehan ret_val = igc_wait_autoneg(hw); 516517904deSPeter Grehan if (ret_val) { 517517904deSPeter Grehan DEBUGOUT("Error while waiting for autoneg to complete\n"); 518517904deSPeter Grehan return ret_val; 519517904deSPeter Grehan } 520517904deSPeter Grehan } 521517904deSPeter Grehan 522517904deSPeter Grehan hw->mac.get_link_status = true; 523517904deSPeter Grehan 524517904deSPeter Grehan return ret_val; 525517904deSPeter Grehan } 526517904deSPeter Grehan 527517904deSPeter Grehan /** 528517904deSPeter Grehan * igc_setup_copper_link_generic - Configure copper link settings 529517904deSPeter Grehan * @hw: pointer to the HW structure 530517904deSPeter Grehan * 531517904deSPeter Grehan * Calls the appropriate function to configure the link for auto-neg or forced 532517904deSPeter Grehan * speed and duplex. Then we check for link, once link is established calls 533517904deSPeter Grehan * to configure collision distance and flow control are called. If link is 534517904deSPeter Grehan * not established, we return -IGC_ERR_PHY (-2). 535517904deSPeter Grehan **/ 536517904deSPeter Grehan s32 igc_setup_copper_link_generic(struct igc_hw *hw) 537517904deSPeter Grehan { 538517904deSPeter Grehan s32 ret_val; 539517904deSPeter Grehan bool link; 540517904deSPeter Grehan 541517904deSPeter Grehan DEBUGFUNC("igc_setup_copper_link_generic"); 542517904deSPeter Grehan 543517904deSPeter Grehan if (hw->mac.autoneg) { 544517904deSPeter Grehan /* Setup autoneg and flow control advertisement and perform 545517904deSPeter Grehan * autonegotiation. 546517904deSPeter Grehan */ 547517904deSPeter Grehan ret_val = igc_copper_link_autoneg(hw); 548517904deSPeter Grehan if (ret_val) 549517904deSPeter Grehan return ret_val; 550517904deSPeter Grehan } else { 551517904deSPeter Grehan /* PHY will be set to 10H, 10F, 100H or 100F 552517904deSPeter Grehan * depending on user settings. 553517904deSPeter Grehan */ 554517904deSPeter Grehan DEBUGOUT("Forcing Speed and Duplex\n"); 555517904deSPeter Grehan ret_val = hw->phy.ops.force_speed_duplex(hw); 556517904deSPeter Grehan if (ret_val) { 557517904deSPeter Grehan DEBUGOUT("Error Forcing Speed and Duplex\n"); 558517904deSPeter Grehan return ret_val; 559517904deSPeter Grehan } 560517904deSPeter Grehan } 561517904deSPeter Grehan 562517904deSPeter Grehan /* Check link status. Wait up to 100 microseconds for link to become 563517904deSPeter Grehan * valid. 564517904deSPeter Grehan */ 565517904deSPeter Grehan ret_val = igc_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, 566517904deSPeter Grehan &link); 567517904deSPeter Grehan if (ret_val) 568517904deSPeter Grehan return ret_val; 569517904deSPeter Grehan 570517904deSPeter Grehan if (link) { 571517904deSPeter Grehan DEBUGOUT("Valid link established!!!\n"); 572517904deSPeter Grehan hw->mac.ops.config_collision_dist(hw); 573517904deSPeter Grehan ret_val = igc_config_fc_after_link_up_generic(hw); 574517904deSPeter Grehan } else { 575517904deSPeter Grehan DEBUGOUT("Unable to establish link!!!\n"); 576517904deSPeter Grehan } 577517904deSPeter Grehan 578517904deSPeter Grehan return ret_val; 579517904deSPeter Grehan } 580517904deSPeter Grehan 581517904deSPeter Grehan /** 582517904deSPeter Grehan * igc_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex 583517904deSPeter Grehan * @hw: pointer to the HW structure 584517904deSPeter Grehan * @phy_ctrl: pointer to current value of PHY_CONTROL 585517904deSPeter Grehan * 586517904deSPeter Grehan * Forces speed and duplex on the PHY by doing the following: disable flow 587517904deSPeter Grehan * control, force speed/duplex on the MAC, disable auto speed detection, 588517904deSPeter Grehan * disable auto-negotiation, configure duplex, configure speed, configure 589517904deSPeter Grehan * the collision distance, write configuration to CTRL register. The 590517904deSPeter Grehan * caller must write to the PHY_CONTROL register for these settings to 591f7c32ed6Sbetterentley * take effect. 592517904deSPeter Grehan **/ 593517904deSPeter Grehan void igc_phy_force_speed_duplex_setup(struct igc_hw *hw, u16 *phy_ctrl) 594517904deSPeter Grehan { 595517904deSPeter Grehan struct igc_mac_info *mac = &hw->mac; 596517904deSPeter Grehan u32 ctrl; 597517904deSPeter Grehan 598517904deSPeter Grehan DEBUGFUNC("igc_phy_force_speed_duplex_setup"); 599517904deSPeter Grehan 600517904deSPeter Grehan /* Turn off flow control when forcing speed/duplex */ 601517904deSPeter Grehan hw->fc.current_mode = igc_fc_none; 602517904deSPeter Grehan 603517904deSPeter Grehan /* Force speed/duplex on the mac */ 604517904deSPeter Grehan ctrl = IGC_READ_REG(hw, IGC_CTRL); 605517904deSPeter Grehan ctrl |= (IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX); 606517904deSPeter Grehan ctrl &= ~IGC_CTRL_SPD_SEL; 607517904deSPeter Grehan 608517904deSPeter Grehan /* Disable Auto Speed Detection */ 609517904deSPeter Grehan ctrl &= ~IGC_CTRL_ASDE; 610517904deSPeter Grehan 611517904deSPeter Grehan /* Disable autoneg on the phy */ 612517904deSPeter Grehan *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; 613517904deSPeter Grehan 614517904deSPeter Grehan /* Forcing Full or Half Duplex? */ 615517904deSPeter Grehan if (mac->forced_speed_duplex & IGC_ALL_HALF_DUPLEX) { 616517904deSPeter Grehan ctrl &= ~IGC_CTRL_FD; 617517904deSPeter Grehan *phy_ctrl &= ~MII_CR_FULL_DUPLEX; 618517904deSPeter Grehan DEBUGOUT("Half Duplex\n"); 619517904deSPeter Grehan } else { 620517904deSPeter Grehan ctrl |= IGC_CTRL_FD; 621517904deSPeter Grehan *phy_ctrl |= MII_CR_FULL_DUPLEX; 622517904deSPeter Grehan DEBUGOUT("Full Duplex\n"); 623517904deSPeter Grehan } 624517904deSPeter Grehan 625517904deSPeter Grehan /* Forcing 10mb or 100mb? */ 626517904deSPeter Grehan if (mac->forced_speed_duplex & IGC_ALL_100_SPEED) { 627517904deSPeter Grehan ctrl |= IGC_CTRL_SPD_100; 628517904deSPeter Grehan *phy_ctrl |= MII_CR_SPEED_100; 629517904deSPeter Grehan *phy_ctrl &= ~MII_CR_SPEED_1000; 630517904deSPeter Grehan DEBUGOUT("Forcing 100mb\n"); 631517904deSPeter Grehan } else { 632517904deSPeter Grehan ctrl &= ~(IGC_CTRL_SPD_1000 | IGC_CTRL_SPD_100); 633517904deSPeter Grehan *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); 634517904deSPeter Grehan DEBUGOUT("Forcing 10mb\n"); 635517904deSPeter Grehan } 636517904deSPeter Grehan 637517904deSPeter Grehan hw->mac.ops.config_collision_dist(hw); 638517904deSPeter Grehan 639517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl); 640517904deSPeter Grehan } 641517904deSPeter Grehan 642517904deSPeter Grehan /** 643517904deSPeter Grehan * igc_set_d3_lplu_state_generic - Sets low power link up state for D3 644517904deSPeter Grehan * @hw: pointer to the HW structure 645517904deSPeter Grehan * @active: boolean used to enable/disable lplu 646517904deSPeter Grehan * 647517904deSPeter Grehan * Success returns 0, Failure returns 1 648517904deSPeter Grehan * 649517904deSPeter Grehan * The low power link up (lplu) state is set to the power management level D3 650517904deSPeter Grehan * and SmartSpeed is disabled when active is true, else clear lplu for D3 651517904deSPeter Grehan * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU 652517904deSPeter Grehan * is used during Dx states where the power conservation is most important. 653517904deSPeter Grehan * During driver activity, SmartSpeed should be enabled so performance is 654517904deSPeter Grehan * maintained. 655517904deSPeter Grehan **/ 656517904deSPeter Grehan s32 igc_set_d3_lplu_state_generic(struct igc_hw *hw, bool active) 657517904deSPeter Grehan { 658517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 659517904deSPeter Grehan s32 ret_val; 660517904deSPeter Grehan u16 data; 661517904deSPeter Grehan 662517904deSPeter Grehan DEBUGFUNC("igc_set_d3_lplu_state_generic"); 663517904deSPeter Grehan 664517904deSPeter Grehan if (!hw->phy.ops.read_reg) 665517904deSPeter Grehan return IGC_SUCCESS; 666517904deSPeter Grehan 667517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, IGP02IGC_PHY_POWER_MGMT, &data); 668517904deSPeter Grehan if (ret_val) 669517904deSPeter Grehan return ret_val; 670517904deSPeter Grehan 671517904deSPeter Grehan if (!active) { 672517904deSPeter Grehan data &= ~IGP02IGC_PM_D3_LPLU; 673517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, 674517904deSPeter Grehan data); 675517904deSPeter Grehan if (ret_val) 676517904deSPeter Grehan return ret_val; 677517904deSPeter Grehan /* LPLU and SmartSpeed are mutually exclusive. LPLU is used 678517904deSPeter Grehan * during Dx states where the power conservation is most 679517904deSPeter Grehan * important. During driver activity we should enable 680517904deSPeter Grehan * SmartSpeed, so performance is maintained. 681517904deSPeter Grehan */ 682517904deSPeter Grehan if (phy->smart_speed == igc_smart_speed_on) { 683517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, 684517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 685517904deSPeter Grehan &data); 686517904deSPeter Grehan if (ret_val) 687517904deSPeter Grehan return ret_val; 688517904deSPeter Grehan 689517904deSPeter Grehan data |= IGP01IGC_PSCFR_SMART_SPEED; 690517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 691517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 692517904deSPeter Grehan data); 693517904deSPeter Grehan if (ret_val) 694517904deSPeter Grehan return ret_val; 695517904deSPeter Grehan } else if (phy->smart_speed == igc_smart_speed_off) { 696517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, 697517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 698517904deSPeter Grehan &data); 699517904deSPeter Grehan if (ret_val) 700517904deSPeter Grehan return ret_val; 701517904deSPeter Grehan 702517904deSPeter Grehan data &= ~IGP01IGC_PSCFR_SMART_SPEED; 703517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 704517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 705517904deSPeter Grehan data); 706517904deSPeter Grehan if (ret_val) 707517904deSPeter Grehan return ret_val; 708517904deSPeter Grehan } 709517904deSPeter Grehan } else if ((phy->autoneg_advertised == IGC_ALL_SPEED_DUPLEX) || 710517904deSPeter Grehan (phy->autoneg_advertised == IGC_ALL_NOT_GIG) || 711517904deSPeter Grehan (phy->autoneg_advertised == IGC_ALL_10_SPEED)) { 712517904deSPeter Grehan data |= IGP02IGC_PM_D3_LPLU; 713517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, 714517904deSPeter Grehan data); 715517904deSPeter Grehan if (ret_val) 716517904deSPeter Grehan return ret_val; 717517904deSPeter Grehan 718517904deSPeter Grehan /* When LPLU is enabled, we should disable SmartSpeed */ 719517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, IGP01IGC_PHY_PORT_CONFIG, 720517904deSPeter Grehan &data); 721517904deSPeter Grehan if (ret_val) 722517904deSPeter Grehan return ret_val; 723517904deSPeter Grehan 724517904deSPeter Grehan data &= ~IGP01IGC_PSCFR_SMART_SPEED; 725517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP01IGC_PHY_PORT_CONFIG, 726517904deSPeter Grehan data); 727517904deSPeter Grehan } 728517904deSPeter Grehan 729517904deSPeter Grehan return ret_val; 730517904deSPeter Grehan } 731517904deSPeter Grehan 732517904deSPeter Grehan /** 733517904deSPeter Grehan * igc_check_downshift_generic - Checks whether a downshift in speed occurred 734517904deSPeter Grehan * @hw: pointer to the HW structure 735517904deSPeter Grehan * 736517904deSPeter Grehan * Success returns 0, Failure returns 1 737517904deSPeter Grehan * 738517904deSPeter Grehan * A downshift is detected by querying the PHY link health. 739517904deSPeter Grehan **/ 740517904deSPeter Grehan s32 igc_check_downshift_generic(struct igc_hw *hw) 741517904deSPeter Grehan { 742517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 743517904deSPeter Grehan s32 ret_val; 744517904deSPeter Grehan 745517904deSPeter Grehan DEBUGFUNC("igc_check_downshift_generic"); 746517904deSPeter Grehan 747517904deSPeter Grehan switch (phy->type) { 748517904deSPeter Grehan case igc_phy_i225: 749517904deSPeter Grehan default: 750517904deSPeter Grehan /* speed downshift not supported */ 751517904deSPeter Grehan phy->speed_downgraded = false; 752517904deSPeter Grehan return IGC_SUCCESS; 753517904deSPeter Grehan } 754517904deSPeter Grehan 755517904deSPeter Grehan return ret_val; 756517904deSPeter Grehan } 757517904deSPeter Grehan 758517904deSPeter Grehan /** 759517904deSPeter Grehan * igc_wait_autoneg - Wait for auto-neg completion 760517904deSPeter Grehan * @hw: pointer to the HW structure 761517904deSPeter Grehan * 762517904deSPeter Grehan * Waits for auto-negotiation to complete or for the auto-negotiation time 763517904deSPeter Grehan * limit to expire, which ever happens first. 764517904deSPeter Grehan **/ 765517904deSPeter Grehan static s32 igc_wait_autoneg(struct igc_hw *hw) 766517904deSPeter Grehan { 767517904deSPeter Grehan s32 ret_val = IGC_SUCCESS; 768517904deSPeter Grehan u16 i, phy_status; 769517904deSPeter Grehan 770517904deSPeter Grehan DEBUGFUNC("igc_wait_autoneg"); 771517904deSPeter Grehan 772517904deSPeter Grehan if (!hw->phy.ops.read_reg) 773517904deSPeter Grehan return IGC_SUCCESS; 774517904deSPeter Grehan 775517904deSPeter Grehan /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ 776517904deSPeter Grehan for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { 777517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 778517904deSPeter Grehan if (ret_val) 779517904deSPeter Grehan break; 780517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 781517904deSPeter Grehan if (ret_val) 782517904deSPeter Grehan break; 783517904deSPeter Grehan if (phy_status & MII_SR_AUTONEG_COMPLETE) 784517904deSPeter Grehan break; 785517904deSPeter Grehan msec_delay(100); 786517904deSPeter Grehan } 787517904deSPeter Grehan 788517904deSPeter Grehan /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation 789517904deSPeter Grehan * has completed. 790517904deSPeter Grehan */ 791517904deSPeter Grehan return ret_val; 792517904deSPeter Grehan } 793517904deSPeter Grehan 794517904deSPeter Grehan /** 795517904deSPeter Grehan * igc_phy_has_link_generic - Polls PHY for link 796517904deSPeter Grehan * @hw: pointer to the HW structure 797517904deSPeter Grehan * @iterations: number of times to poll for link 798517904deSPeter Grehan * @usec_interval: delay between polling attempts 799517904deSPeter Grehan * @success: pointer to whether polling was successful or not 800517904deSPeter Grehan * 801517904deSPeter Grehan * Polls the PHY status register for link, 'iterations' number of times. 802517904deSPeter Grehan **/ 803517904deSPeter Grehan s32 igc_phy_has_link_generic(struct igc_hw *hw, u32 iterations, 804517904deSPeter Grehan u32 usec_interval, bool *success) 805517904deSPeter Grehan { 806517904deSPeter Grehan s32 ret_val = IGC_SUCCESS; 807517904deSPeter Grehan u16 i, phy_status; 808517904deSPeter Grehan 809517904deSPeter Grehan DEBUGFUNC("igc_phy_has_link_generic"); 810517904deSPeter Grehan 811517904deSPeter Grehan if (!hw->phy.ops.read_reg) 812517904deSPeter Grehan return IGC_SUCCESS; 813517904deSPeter Grehan 814517904deSPeter Grehan for (i = 0; i < iterations; i++) { 815517904deSPeter Grehan /* Some PHYs require the PHY_STATUS register to be read 816517904deSPeter Grehan * twice due to the link bit being sticky. No harm doing 817517904deSPeter Grehan * it across the board. 818517904deSPeter Grehan */ 819517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 820517904deSPeter Grehan if (ret_val) { 821517904deSPeter Grehan /* If the first read fails, another entity may have 822517904deSPeter Grehan * ownership of the resources, wait and try again to 823517904deSPeter Grehan * see if they have relinquished the resources yet. 824517904deSPeter Grehan */ 825517904deSPeter Grehan if (usec_interval >= 1000) 826517904deSPeter Grehan msec_delay(usec_interval/1000); 827517904deSPeter Grehan else 828517904deSPeter Grehan usec_delay(usec_interval); 829517904deSPeter Grehan } 830517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 831517904deSPeter Grehan if (ret_val) 832517904deSPeter Grehan break; 833517904deSPeter Grehan if (phy_status & MII_SR_LINK_STATUS) 834517904deSPeter Grehan break; 835517904deSPeter Grehan if (usec_interval >= 1000) 836517904deSPeter Grehan msec_delay(usec_interval/1000); 837517904deSPeter Grehan else 838517904deSPeter Grehan usec_delay(usec_interval); 839517904deSPeter Grehan } 840517904deSPeter Grehan 841517904deSPeter Grehan *success = (i < iterations); 842517904deSPeter Grehan 843517904deSPeter Grehan return ret_val; 844517904deSPeter Grehan } 845517904deSPeter Grehan 846517904deSPeter Grehan /** 847517904deSPeter Grehan * igc_phy_hw_reset_generic - PHY hardware reset 848517904deSPeter Grehan * @hw: pointer to the HW structure 849517904deSPeter Grehan * 850517904deSPeter Grehan * Verify the reset block is not blocking us from resetting. Acquire 851517904deSPeter Grehan * semaphore (if necessary) and read/set/write the device control reset 852517904deSPeter Grehan * bit in the PHY. Wait the appropriate delay time for the device to 853517904deSPeter Grehan * reset and release the semaphore (if necessary). 854517904deSPeter Grehan **/ 855517904deSPeter Grehan s32 igc_phy_hw_reset_generic(struct igc_hw *hw) 856517904deSPeter Grehan { 857517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 858517904deSPeter Grehan s32 ret_val; 859517904deSPeter Grehan u32 ctrl, timeout = 10000, phpm = 0; 860517904deSPeter Grehan 861517904deSPeter Grehan DEBUGFUNC("igc_phy_hw_reset_generic"); 862517904deSPeter Grehan 863517904deSPeter Grehan if (phy->ops.check_reset_block) { 864517904deSPeter Grehan ret_val = phy->ops.check_reset_block(hw); 865517904deSPeter Grehan if (ret_val) 866517904deSPeter Grehan return IGC_SUCCESS; 867517904deSPeter Grehan } 868517904deSPeter Grehan 869517904deSPeter Grehan ret_val = phy->ops.acquire(hw); 870517904deSPeter Grehan if (ret_val) 871517904deSPeter Grehan return ret_val; 872517904deSPeter Grehan 873517904deSPeter Grehan phpm = IGC_READ_REG(hw, IGC_I225_PHPM); 874517904deSPeter Grehan 875517904deSPeter Grehan ctrl = IGC_READ_REG(hw, IGC_CTRL); 876517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl | IGC_CTRL_PHY_RST); 877517904deSPeter Grehan IGC_WRITE_FLUSH(hw); 878517904deSPeter Grehan 879517904deSPeter Grehan usec_delay(phy->reset_delay_us); 880517904deSPeter Grehan 881517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl); 882517904deSPeter Grehan IGC_WRITE_FLUSH(hw); 883517904deSPeter Grehan 884517904deSPeter Grehan usec_delay(150); 885517904deSPeter Grehan 886517904deSPeter Grehan do { 887517904deSPeter Grehan phpm = IGC_READ_REG(hw, IGC_I225_PHPM); 888517904deSPeter Grehan timeout--; 889517904deSPeter Grehan usec_delay(1); 890517904deSPeter Grehan } while (!(phpm & IGC_I225_PHPM_RST_COMPL) && timeout); 891517904deSPeter Grehan 892517904deSPeter Grehan if (!timeout) 893517904deSPeter Grehan DEBUGOUT("Timeout expired after a phy reset\n"); 894517904deSPeter Grehan 895517904deSPeter Grehan phy->ops.release(hw); 896517904deSPeter Grehan 897517904deSPeter Grehan return ret_val; 898517904deSPeter Grehan } 899517904deSPeter Grehan 900517904deSPeter Grehan /** 901517904deSPeter Grehan * igc_power_up_phy_copper - Restore copper link in case of PHY power down 902517904deSPeter Grehan * @hw: pointer to the HW structure 903517904deSPeter Grehan * 904517904deSPeter Grehan * In the case of a PHY power down to save power, or to turn off link during a 905517904deSPeter Grehan * driver unload, or wake on lan is not enabled, restore the link to previous 906517904deSPeter Grehan * settings. 907517904deSPeter Grehan **/ 908517904deSPeter Grehan void igc_power_up_phy_copper(struct igc_hw *hw) 909517904deSPeter Grehan { 910517904deSPeter Grehan u16 mii_reg = 0; 911517904deSPeter Grehan 912517904deSPeter Grehan /* The PHY will retain its settings across a power down/up cycle */ 913517904deSPeter Grehan hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 914517904deSPeter Grehan mii_reg &= ~MII_CR_POWER_DOWN; 915517904deSPeter Grehan hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 916517904deSPeter Grehan usec_delay(300); 917517904deSPeter Grehan } 918517904deSPeter Grehan 919517904deSPeter Grehan /** 920517904deSPeter Grehan * igc_power_down_phy_copper - Restore copper link in case of PHY power down 921517904deSPeter Grehan * @hw: pointer to the HW structure 922517904deSPeter Grehan * 923517904deSPeter Grehan * In the case of a PHY power down to save power, or to turn off link during a 924517904deSPeter Grehan * driver unload, or wake on lan is not enabled, restore the link to previous 925517904deSPeter Grehan * settings. 926517904deSPeter Grehan **/ 927517904deSPeter Grehan void igc_power_down_phy_copper(struct igc_hw *hw) 928517904deSPeter Grehan { 929517904deSPeter Grehan u16 mii_reg = 0; 930517904deSPeter Grehan 931517904deSPeter Grehan /* The PHY will retain its settings across a power down/up cycle */ 932517904deSPeter Grehan hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 933517904deSPeter Grehan mii_reg |= MII_CR_POWER_DOWN; 934517904deSPeter Grehan hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 935517904deSPeter Grehan msec_delay(1); 936517904deSPeter Grehan } 937517904deSPeter Grehan /** 938517904deSPeter Grehan * igc_write_phy_reg_gpy - Write GPY PHY register 939517904deSPeter Grehan * @hw: pointer to the HW structure 940517904deSPeter Grehan * @offset: register offset to write to 941517904deSPeter Grehan * @data: data to write at register offset 942517904deSPeter Grehan * 943517904deSPeter Grehan * Acquires semaphore, if necessary, then writes the data to PHY register 944517904deSPeter Grehan * at the offset. Release any acquired semaphores before exiting. 945517904deSPeter Grehan **/ 946517904deSPeter Grehan s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data) 947517904deSPeter Grehan { 948517904deSPeter Grehan s32 ret_val; 949517904deSPeter Grehan u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 950517904deSPeter Grehan 951517904deSPeter Grehan DEBUGFUNC("igc_write_phy_reg_gpy"); 952517904deSPeter Grehan 953517904deSPeter Grehan offset = offset & GPY_REG_MASK; 954517904deSPeter Grehan 955517904deSPeter Grehan if (!dev_addr) { 956517904deSPeter Grehan ret_val = hw->phy.ops.acquire(hw); 957517904deSPeter Grehan if (ret_val) 958517904deSPeter Grehan return ret_val; 959517904deSPeter Grehan ret_val = igc_write_phy_reg_mdic(hw, offset, data); 960517904deSPeter Grehan if (ret_val) 961517904deSPeter Grehan return ret_val; 962517904deSPeter Grehan hw->phy.ops.release(hw); 963517904deSPeter Grehan } else { 964517904deSPeter Grehan ret_val = igc_write_xmdio_reg(hw, (u16)offset, dev_addr, 965517904deSPeter Grehan data); 966517904deSPeter Grehan } 967517904deSPeter Grehan return ret_val; 968517904deSPeter Grehan } 969517904deSPeter Grehan 970517904deSPeter Grehan /** 971517904deSPeter Grehan * igc_read_phy_reg_gpy - Read GPY PHY register 972517904deSPeter Grehan * @hw: pointer to the HW structure 973517904deSPeter Grehan * @offset: lower half is register offset to read to 974517904deSPeter Grehan * upper half is MMD to use. 975517904deSPeter Grehan * @data: data to read at register offset 976517904deSPeter Grehan * 977517904deSPeter Grehan * Acquires semaphore, if necessary, then reads the data in the PHY register 978517904deSPeter Grehan * at the offset. Release any acquired semaphores before exiting. 979517904deSPeter Grehan **/ 980517904deSPeter Grehan s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data) 981517904deSPeter Grehan { 982517904deSPeter Grehan s32 ret_val; 983517904deSPeter Grehan u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 984517904deSPeter Grehan 985517904deSPeter Grehan DEBUGFUNC("igc_read_phy_reg_gpy"); 986517904deSPeter Grehan 987517904deSPeter Grehan offset = offset & GPY_REG_MASK; 988517904deSPeter Grehan 989517904deSPeter Grehan if (!dev_addr) { 990517904deSPeter Grehan ret_val = hw->phy.ops.acquire(hw); 991517904deSPeter Grehan if (ret_val) 992517904deSPeter Grehan return ret_val; 993517904deSPeter Grehan ret_val = igc_read_phy_reg_mdic(hw, offset, data); 994517904deSPeter Grehan if (ret_val) 995517904deSPeter Grehan return ret_val; 996517904deSPeter Grehan hw->phy.ops.release(hw); 997517904deSPeter Grehan } else { 998517904deSPeter Grehan ret_val = igc_read_xmdio_reg(hw, (u16)offset, dev_addr, 999517904deSPeter Grehan data); 1000517904deSPeter Grehan } 1001517904deSPeter Grehan return ret_val; 1002517904deSPeter Grehan } 1003517904deSPeter Grehan 1004517904deSPeter Grehan 1005517904deSPeter Grehan /** 1006517904deSPeter Grehan * __igc_access_xmdio_reg - Read/write XMDIO register 1007517904deSPeter Grehan * @hw: pointer to the HW structure 1008517904deSPeter Grehan * @address: XMDIO address to program 1009517904deSPeter Grehan * @dev_addr: device address to program 1010517904deSPeter Grehan * @data: pointer to value to read/write from/to the XMDIO address 1011517904deSPeter Grehan * @read: boolean flag to indicate read or write 1012517904deSPeter Grehan **/ 1013517904deSPeter Grehan static s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address, 1014517904deSPeter Grehan u8 dev_addr, u16 *data, bool read) 1015517904deSPeter Grehan { 1016517904deSPeter Grehan s32 ret_val; 1017517904deSPeter Grehan 1018517904deSPeter Grehan DEBUGFUNC("__igc_access_xmdio_reg"); 1019517904deSPeter Grehan 1020517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr); 1021517904deSPeter Grehan if (ret_val) 1022517904deSPeter Grehan return ret_val; 1023517904deSPeter Grehan 1024517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address); 1025517904deSPeter Grehan if (ret_val) 1026517904deSPeter Grehan return ret_val; 1027517904deSPeter Grehan 1028517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA | 1029517904deSPeter Grehan dev_addr); 1030517904deSPeter Grehan if (ret_val) 1031517904deSPeter Grehan return ret_val; 1032517904deSPeter Grehan 1033517904deSPeter Grehan if (read) 1034517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data); 1035517904deSPeter Grehan else 1036517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data); 1037517904deSPeter Grehan if (ret_val) 1038517904deSPeter Grehan return ret_val; 1039517904deSPeter Grehan 1040517904deSPeter Grehan /* Recalibrate the device back to 0 */ 1041517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0); 1042517904deSPeter Grehan if (ret_val) 1043517904deSPeter Grehan return ret_val; 1044517904deSPeter Grehan 1045517904deSPeter Grehan return ret_val; 1046517904deSPeter Grehan } 1047517904deSPeter Grehan 1048517904deSPeter Grehan /** 1049517904deSPeter Grehan * igc_read_xmdio_reg - Read XMDIO register 1050517904deSPeter Grehan * @hw: pointer to the HW structure 1051517904deSPeter Grehan * @addr: XMDIO address to program 1052517904deSPeter Grehan * @dev_addr: device address to program 1053517904deSPeter Grehan * @data: value to be read from the EMI address 1054517904deSPeter Grehan **/ 1055517904deSPeter Grehan s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 *data) 1056517904deSPeter Grehan { 1057517904deSPeter Grehan DEBUGFUNC("igc_read_xmdio_reg"); 1058517904deSPeter Grehan 1059517904deSPeter Grehan return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true); 1060517904deSPeter Grehan } 1061517904deSPeter Grehan 1062517904deSPeter Grehan /** 1063517904deSPeter Grehan * igc_write_xmdio_reg - Write XMDIO register 1064517904deSPeter Grehan * @hw: pointer to the HW structure 1065517904deSPeter Grehan * @addr: XMDIO address to program 1066517904deSPeter Grehan * @dev_addr: device address to program 1067517904deSPeter Grehan * @data: value to be written to the XMDIO address 1068517904deSPeter Grehan **/ 1069517904deSPeter Grehan s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 data) 1070517904deSPeter Grehan { 1071517904deSPeter Grehan DEBUGFUNC("igc_write_xmdio_reg"); 1072517904deSPeter Grehan 1073517904deSPeter Grehan return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false); 1074517904deSPeter Grehan } 1075