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); 149*561cd74bSPeter 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 313517904deSPeter Grehan if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && 314517904deSPeter Grehan hw->phy.id == I225_I_PHY_ID) { 315517904deSPeter Grehan /* Read the MULTI GBT AN Control Register - reg 7.32 */ 316517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK << 317517904deSPeter Grehan MMD_DEVADDR_SHIFT) | 318517904deSPeter Grehan ANEG_MULTIGBT_AN_CTRL, 319517904deSPeter Grehan &aneg_multigbt_an_ctrl); 320517904deSPeter Grehan 321517904deSPeter Grehan if (ret_val) 322517904deSPeter Grehan return ret_val; 323517904deSPeter Grehan } 324517904deSPeter Grehan 325517904deSPeter Grehan /* Need to parse both autoneg_advertised and fc and set up 326517904deSPeter Grehan * the appropriate PHY registers. First we will parse for 327517904deSPeter Grehan * autoneg_advertised software override. Since we can advertise 328517904deSPeter Grehan * a plethora of combinations, we need to check each bit 329517904deSPeter Grehan * individually. 330517904deSPeter Grehan */ 331517904deSPeter Grehan 332517904deSPeter Grehan /* First we clear all the 10/100 mb speed bits in the Auto-Neg 333517904deSPeter Grehan * Advertisement Register (Address 4) and the 1000 mb speed bits in 334517904deSPeter Grehan * the 1000Base-T Control Register (Address 9). 335517904deSPeter Grehan */ 336517904deSPeter Grehan mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | 337517904deSPeter Grehan NWAY_AR_100TX_HD_CAPS | 338517904deSPeter Grehan NWAY_AR_10T_FD_CAPS | 339517904deSPeter Grehan NWAY_AR_10T_HD_CAPS); 340517904deSPeter Grehan mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); 341517904deSPeter Grehan 342517904deSPeter Grehan DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised); 343517904deSPeter Grehan 344517904deSPeter Grehan /* Do we want to advertise 10 Mb Half Duplex? */ 345517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_10_HALF) { 346517904deSPeter Grehan DEBUGOUT("Advertise 10mb Half duplex\n"); 347517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; 348517904deSPeter Grehan } 349517904deSPeter Grehan 350517904deSPeter Grehan /* Do we want to advertise 10 Mb Full Duplex? */ 351517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_10_FULL) { 352517904deSPeter Grehan DEBUGOUT("Advertise 10mb Full duplex\n"); 353517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; 354517904deSPeter Grehan } 355517904deSPeter Grehan 356517904deSPeter Grehan /* Do we want to advertise 100 Mb Half Duplex? */ 357517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_100_HALF) { 358517904deSPeter Grehan DEBUGOUT("Advertise 100mb Half duplex\n"); 359517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; 360517904deSPeter Grehan } 361517904deSPeter Grehan 362517904deSPeter Grehan /* Do we want to advertise 100 Mb Full Duplex? */ 363517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_100_FULL) { 364517904deSPeter Grehan DEBUGOUT("Advertise 100mb Full duplex\n"); 365517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; 366517904deSPeter Grehan } 367517904deSPeter Grehan 368517904deSPeter Grehan /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ 369517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_1000_HALF) 370517904deSPeter Grehan DEBUGOUT("Advertise 1000mb Half duplex request denied!\n"); 371517904deSPeter Grehan 372517904deSPeter Grehan /* Do we want to advertise 1000 Mb Full Duplex? */ 373517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { 374517904deSPeter Grehan DEBUGOUT("Advertise 1000mb Full duplex\n"); 375517904deSPeter Grehan mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; 376517904deSPeter Grehan } 377517904deSPeter Grehan 378517904deSPeter Grehan /* We do not allow the Phy to advertise 2500 Mb Half Duplex */ 379517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_2500_HALF) 380517904deSPeter Grehan DEBUGOUT("Advertise 2500mb Half duplex request denied!\n"); 381517904deSPeter Grehan 382517904deSPeter Grehan /* Do we want to advertise 2500 Mb Full Duplex? */ 383517904deSPeter Grehan if (phy->autoneg_advertised & ADVERTISE_2500_FULL) { 384517904deSPeter Grehan DEBUGOUT("Advertise 2500mb Full duplex\n"); 385517904deSPeter Grehan aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS; 386517904deSPeter Grehan } else { 387517904deSPeter Grehan aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS; 388517904deSPeter Grehan } 389517904deSPeter Grehan 390517904deSPeter Grehan /* Check for a software override of the flow control settings, and 391517904deSPeter Grehan * setup the PHY advertisement registers accordingly. If 392517904deSPeter Grehan * auto-negotiation is enabled, then software will have to set the 393517904deSPeter Grehan * "PAUSE" bits to the correct value in the Auto-Negotiation 394517904deSPeter Grehan * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- 395517904deSPeter Grehan * negotiation. 396517904deSPeter Grehan * 397517904deSPeter Grehan * The possible values of the "fc" parameter are: 398517904deSPeter Grehan * 0: Flow control is completely disabled 399517904deSPeter Grehan * 1: Rx flow control is enabled (we can receive pause frames 400517904deSPeter Grehan * but not send pause frames). 401517904deSPeter Grehan * 2: Tx flow control is enabled (we can send pause frames 402517904deSPeter Grehan * but we do not support receiving pause frames). 403517904deSPeter Grehan * 3: Both Rx and Tx flow control (symmetric) are enabled. 404517904deSPeter Grehan * other: No software override. The flow control configuration 405517904deSPeter Grehan * in the EEPROM is used. 406517904deSPeter Grehan */ 407517904deSPeter Grehan switch (hw->fc.current_mode) { 408517904deSPeter Grehan case igc_fc_none: 409517904deSPeter Grehan /* Flow control (Rx & Tx) is completely disabled by a 410517904deSPeter Grehan * software over-ride. 411517904deSPeter Grehan */ 412517904deSPeter Grehan mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 413517904deSPeter Grehan break; 414517904deSPeter Grehan case igc_fc_rx_pause: 415517904deSPeter Grehan /* Rx Flow control is enabled, and Tx Flow control is 416517904deSPeter Grehan * disabled, by a software over-ride. 417517904deSPeter Grehan * 418517904deSPeter Grehan * Since there really isn't a way to advertise that we are 419517904deSPeter Grehan * capable of Rx Pause ONLY, we will advertise that we 420517904deSPeter Grehan * support both symmetric and asymmetric Rx PAUSE. Later 421517904deSPeter Grehan * (in igc_config_fc_after_link_up) we will disable the 422517904deSPeter Grehan * hw's ability to send PAUSE frames. 423517904deSPeter Grehan */ 424517904deSPeter Grehan mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 425517904deSPeter Grehan break; 426517904deSPeter Grehan case igc_fc_tx_pause: 427517904deSPeter Grehan /* Tx Flow control is enabled, and Rx Flow control is 428517904deSPeter Grehan * disabled, by a software over-ride. 429517904deSPeter Grehan */ 430517904deSPeter Grehan mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; 431517904deSPeter Grehan mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; 432517904deSPeter Grehan break; 433517904deSPeter Grehan case igc_fc_full: 434517904deSPeter Grehan /* Flow control (both Rx and Tx) is enabled by a software 435517904deSPeter Grehan * over-ride. 436517904deSPeter Grehan */ 437517904deSPeter Grehan mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 438517904deSPeter Grehan break; 439517904deSPeter Grehan default: 440517904deSPeter Grehan DEBUGOUT("Flow control param set incorrectly\n"); 441517904deSPeter Grehan return -IGC_ERR_CONFIG; 442517904deSPeter Grehan } 443517904deSPeter Grehan 444517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); 445517904deSPeter Grehan if (ret_val) 446517904deSPeter Grehan return ret_val; 447517904deSPeter Grehan 448517904deSPeter Grehan DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); 449517904deSPeter Grehan 450517904deSPeter Grehan if (phy->autoneg_mask & ADVERTISE_1000_FULL) 451517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, 452517904deSPeter Grehan mii_1000t_ctrl_reg); 453517904deSPeter Grehan 454517904deSPeter Grehan if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && 455517904deSPeter Grehan hw->phy.id == I225_I_PHY_ID) 456517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 457517904deSPeter Grehan (STANDARD_AN_REG_MASK << 458517904deSPeter Grehan MMD_DEVADDR_SHIFT) | 459517904deSPeter Grehan ANEG_MULTIGBT_AN_CTRL, 460517904deSPeter Grehan aneg_multigbt_an_ctrl); 461517904deSPeter Grehan 462517904deSPeter Grehan return ret_val; 463517904deSPeter Grehan } 464517904deSPeter Grehan 465517904deSPeter Grehan /** 466517904deSPeter Grehan * igc_copper_link_autoneg - Setup/Enable autoneg for copper link 467517904deSPeter Grehan * @hw: pointer to the HW structure 468517904deSPeter Grehan * 469517904deSPeter Grehan * Performs initial bounds checking on autoneg advertisement parameter, then 470517904deSPeter Grehan * configure to advertise the full capability. Setup the PHY to autoneg 471517904deSPeter Grehan * and restart the negotiation process between the link partner. If 472517904deSPeter Grehan * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. 473517904deSPeter Grehan **/ 474517904deSPeter Grehan static s32 igc_copper_link_autoneg(struct igc_hw *hw) 475517904deSPeter Grehan { 476517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 477517904deSPeter Grehan s32 ret_val; 478517904deSPeter Grehan u16 phy_ctrl; 479517904deSPeter Grehan 480517904deSPeter Grehan DEBUGFUNC("igc_copper_link_autoneg"); 481517904deSPeter Grehan 482517904deSPeter Grehan /* Perform some bounds checking on the autoneg advertisement 483517904deSPeter Grehan * parameter. 484517904deSPeter Grehan */ 485517904deSPeter Grehan phy->autoneg_advertised &= phy->autoneg_mask; 486517904deSPeter Grehan 487517904deSPeter Grehan /* If autoneg_advertised is zero, we assume it was not defaulted 488517904deSPeter Grehan * by the calling code so we set to advertise full capability. 489517904deSPeter Grehan */ 490517904deSPeter Grehan if (!phy->autoneg_advertised) 491517904deSPeter Grehan phy->autoneg_advertised = phy->autoneg_mask; 492517904deSPeter Grehan 493517904deSPeter Grehan DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); 494517904deSPeter Grehan ret_val = igc_phy_setup_autoneg(hw); 495517904deSPeter Grehan if (ret_val) { 496517904deSPeter Grehan DEBUGOUT("Error Setting up Auto-Negotiation\n"); 497517904deSPeter Grehan return ret_val; 498517904deSPeter Grehan } 499517904deSPeter Grehan DEBUGOUT("Restarting Auto-Neg\n"); 500517904deSPeter Grehan 501517904deSPeter Grehan /* Restart auto-negotiation by setting the Auto Neg Enable bit and 502517904deSPeter Grehan * the Auto Neg Restart bit in the PHY control register. 503517904deSPeter Grehan */ 504517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); 505517904deSPeter Grehan if (ret_val) 506517904deSPeter Grehan return ret_val; 507517904deSPeter Grehan 508517904deSPeter Grehan phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); 509517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); 510517904deSPeter Grehan if (ret_val) 511517904deSPeter Grehan return ret_val; 512517904deSPeter Grehan 513517904deSPeter Grehan /* Does the user want to wait for Auto-Neg to complete here, or 514517904deSPeter Grehan * check at a later time (for example, callback routine). 515517904deSPeter Grehan */ 516517904deSPeter Grehan if (phy->autoneg_wait_to_complete) { 517517904deSPeter Grehan ret_val = igc_wait_autoneg(hw); 518517904deSPeter Grehan if (ret_val) { 519517904deSPeter Grehan DEBUGOUT("Error while waiting for autoneg to complete\n"); 520517904deSPeter Grehan return ret_val; 521517904deSPeter Grehan } 522517904deSPeter Grehan } 523517904deSPeter Grehan 524517904deSPeter Grehan hw->mac.get_link_status = true; 525517904deSPeter Grehan 526517904deSPeter Grehan return ret_val; 527517904deSPeter Grehan } 528517904deSPeter Grehan 529517904deSPeter Grehan /** 530517904deSPeter Grehan * igc_setup_copper_link_generic - Configure copper link settings 531517904deSPeter Grehan * @hw: pointer to the HW structure 532517904deSPeter Grehan * 533517904deSPeter Grehan * Calls the appropriate function to configure the link for auto-neg or forced 534517904deSPeter Grehan * speed and duplex. Then we check for link, once link is established calls 535517904deSPeter Grehan * to configure collision distance and flow control are called. If link is 536517904deSPeter Grehan * not established, we return -IGC_ERR_PHY (-2). 537517904deSPeter Grehan **/ 538517904deSPeter Grehan s32 igc_setup_copper_link_generic(struct igc_hw *hw) 539517904deSPeter Grehan { 540517904deSPeter Grehan s32 ret_val; 541517904deSPeter Grehan bool link; 542517904deSPeter Grehan 543517904deSPeter Grehan DEBUGFUNC("igc_setup_copper_link_generic"); 544517904deSPeter Grehan 545517904deSPeter Grehan if (hw->mac.autoneg) { 546517904deSPeter Grehan /* Setup autoneg and flow control advertisement and perform 547517904deSPeter Grehan * autonegotiation. 548517904deSPeter Grehan */ 549517904deSPeter Grehan ret_val = igc_copper_link_autoneg(hw); 550517904deSPeter Grehan if (ret_val) 551517904deSPeter Grehan return ret_val; 552517904deSPeter Grehan } else { 553517904deSPeter Grehan /* PHY will be set to 10H, 10F, 100H or 100F 554517904deSPeter Grehan * depending on user settings. 555517904deSPeter Grehan */ 556517904deSPeter Grehan DEBUGOUT("Forcing Speed and Duplex\n"); 557517904deSPeter Grehan ret_val = hw->phy.ops.force_speed_duplex(hw); 558517904deSPeter Grehan if (ret_val) { 559517904deSPeter Grehan DEBUGOUT("Error Forcing Speed and Duplex\n"); 560517904deSPeter Grehan return ret_val; 561517904deSPeter Grehan } 562517904deSPeter Grehan } 563517904deSPeter Grehan 564517904deSPeter Grehan /* Check link status. Wait up to 100 microseconds for link to become 565517904deSPeter Grehan * valid. 566517904deSPeter Grehan */ 567517904deSPeter Grehan ret_val = igc_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, 568517904deSPeter Grehan &link); 569517904deSPeter Grehan if (ret_val) 570517904deSPeter Grehan return ret_val; 571517904deSPeter Grehan 572517904deSPeter Grehan if (link) { 573517904deSPeter Grehan DEBUGOUT("Valid link established!!!\n"); 574517904deSPeter Grehan hw->mac.ops.config_collision_dist(hw); 575517904deSPeter Grehan ret_val = igc_config_fc_after_link_up_generic(hw); 576517904deSPeter Grehan } else { 577517904deSPeter Grehan DEBUGOUT("Unable to establish link!!!\n"); 578517904deSPeter Grehan } 579517904deSPeter Grehan 580517904deSPeter Grehan return ret_val; 581517904deSPeter Grehan } 582517904deSPeter Grehan 583517904deSPeter Grehan /** 584517904deSPeter Grehan * igc_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex 585517904deSPeter Grehan * @hw: pointer to the HW structure 586517904deSPeter Grehan * @phy_ctrl: pointer to current value of PHY_CONTROL 587517904deSPeter Grehan * 588517904deSPeter Grehan * Forces speed and duplex on the PHY by doing the following: disable flow 589517904deSPeter Grehan * control, force speed/duplex on the MAC, disable auto speed detection, 590517904deSPeter Grehan * disable auto-negotiation, configure duplex, configure speed, configure 591517904deSPeter Grehan * the collision distance, write configuration to CTRL register. The 592517904deSPeter Grehan * caller must write to the PHY_CONTROL register for these settings to 593517904deSPeter Grehan * take affect. 594517904deSPeter Grehan **/ 595517904deSPeter Grehan void igc_phy_force_speed_duplex_setup(struct igc_hw *hw, u16 *phy_ctrl) 596517904deSPeter Grehan { 597517904deSPeter Grehan struct igc_mac_info *mac = &hw->mac; 598517904deSPeter Grehan u32 ctrl; 599517904deSPeter Grehan 600517904deSPeter Grehan DEBUGFUNC("igc_phy_force_speed_duplex_setup"); 601517904deSPeter Grehan 602517904deSPeter Grehan /* Turn off flow control when forcing speed/duplex */ 603517904deSPeter Grehan hw->fc.current_mode = igc_fc_none; 604517904deSPeter Grehan 605517904deSPeter Grehan /* Force speed/duplex on the mac */ 606517904deSPeter Grehan ctrl = IGC_READ_REG(hw, IGC_CTRL); 607517904deSPeter Grehan ctrl |= (IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX); 608517904deSPeter Grehan ctrl &= ~IGC_CTRL_SPD_SEL; 609517904deSPeter Grehan 610517904deSPeter Grehan /* Disable Auto Speed Detection */ 611517904deSPeter Grehan ctrl &= ~IGC_CTRL_ASDE; 612517904deSPeter Grehan 613517904deSPeter Grehan /* Disable autoneg on the phy */ 614517904deSPeter Grehan *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; 615517904deSPeter Grehan 616517904deSPeter Grehan /* Forcing Full or Half Duplex? */ 617517904deSPeter Grehan if (mac->forced_speed_duplex & IGC_ALL_HALF_DUPLEX) { 618517904deSPeter Grehan ctrl &= ~IGC_CTRL_FD; 619517904deSPeter Grehan *phy_ctrl &= ~MII_CR_FULL_DUPLEX; 620517904deSPeter Grehan DEBUGOUT("Half Duplex\n"); 621517904deSPeter Grehan } else { 622517904deSPeter Grehan ctrl |= IGC_CTRL_FD; 623517904deSPeter Grehan *phy_ctrl |= MII_CR_FULL_DUPLEX; 624517904deSPeter Grehan DEBUGOUT("Full Duplex\n"); 625517904deSPeter Grehan } 626517904deSPeter Grehan 627517904deSPeter Grehan /* Forcing 10mb or 100mb? */ 628517904deSPeter Grehan if (mac->forced_speed_duplex & IGC_ALL_100_SPEED) { 629517904deSPeter Grehan ctrl |= IGC_CTRL_SPD_100; 630517904deSPeter Grehan *phy_ctrl |= MII_CR_SPEED_100; 631517904deSPeter Grehan *phy_ctrl &= ~MII_CR_SPEED_1000; 632517904deSPeter Grehan DEBUGOUT("Forcing 100mb\n"); 633517904deSPeter Grehan } else { 634517904deSPeter Grehan ctrl &= ~(IGC_CTRL_SPD_1000 | IGC_CTRL_SPD_100); 635517904deSPeter Grehan *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); 636517904deSPeter Grehan DEBUGOUT("Forcing 10mb\n"); 637517904deSPeter Grehan } 638517904deSPeter Grehan 639517904deSPeter Grehan hw->mac.ops.config_collision_dist(hw); 640517904deSPeter Grehan 641517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl); 642517904deSPeter Grehan } 643517904deSPeter Grehan 644517904deSPeter Grehan /** 645517904deSPeter Grehan * igc_set_d3_lplu_state_generic - Sets low power link up state for D3 646517904deSPeter Grehan * @hw: pointer to the HW structure 647517904deSPeter Grehan * @active: boolean used to enable/disable lplu 648517904deSPeter Grehan * 649517904deSPeter Grehan * Success returns 0, Failure returns 1 650517904deSPeter Grehan * 651517904deSPeter Grehan * The low power link up (lplu) state is set to the power management level D3 652517904deSPeter Grehan * and SmartSpeed is disabled when active is true, else clear lplu for D3 653517904deSPeter Grehan * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU 654517904deSPeter Grehan * is used during Dx states where the power conservation is most important. 655517904deSPeter Grehan * During driver activity, SmartSpeed should be enabled so performance is 656517904deSPeter Grehan * maintained. 657517904deSPeter Grehan **/ 658517904deSPeter Grehan s32 igc_set_d3_lplu_state_generic(struct igc_hw *hw, bool active) 659517904deSPeter Grehan { 660517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 661517904deSPeter Grehan s32 ret_val; 662517904deSPeter Grehan u16 data; 663517904deSPeter Grehan 664517904deSPeter Grehan DEBUGFUNC("igc_set_d3_lplu_state_generic"); 665517904deSPeter Grehan 666517904deSPeter Grehan if (!hw->phy.ops.read_reg) 667517904deSPeter Grehan return IGC_SUCCESS; 668517904deSPeter Grehan 669517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, IGP02IGC_PHY_POWER_MGMT, &data); 670517904deSPeter Grehan if (ret_val) 671517904deSPeter Grehan return ret_val; 672517904deSPeter Grehan 673517904deSPeter Grehan if (!active) { 674517904deSPeter Grehan data &= ~IGP02IGC_PM_D3_LPLU; 675517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, 676517904deSPeter Grehan data); 677517904deSPeter Grehan if (ret_val) 678517904deSPeter Grehan return ret_val; 679517904deSPeter Grehan /* LPLU and SmartSpeed are mutually exclusive. LPLU is used 680517904deSPeter Grehan * during Dx states where the power conservation is most 681517904deSPeter Grehan * important. During driver activity we should enable 682517904deSPeter Grehan * SmartSpeed, so performance is maintained. 683517904deSPeter Grehan */ 684517904deSPeter Grehan if (phy->smart_speed == igc_smart_speed_on) { 685517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, 686517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 687517904deSPeter Grehan &data); 688517904deSPeter Grehan if (ret_val) 689517904deSPeter Grehan return ret_val; 690517904deSPeter Grehan 691517904deSPeter Grehan data |= IGP01IGC_PSCFR_SMART_SPEED; 692517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 693517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 694517904deSPeter Grehan data); 695517904deSPeter Grehan if (ret_val) 696517904deSPeter Grehan return ret_val; 697517904deSPeter Grehan } else if (phy->smart_speed == igc_smart_speed_off) { 698517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, 699517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 700517904deSPeter Grehan &data); 701517904deSPeter Grehan if (ret_val) 702517904deSPeter Grehan return ret_val; 703517904deSPeter Grehan 704517904deSPeter Grehan data &= ~IGP01IGC_PSCFR_SMART_SPEED; 705517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, 706517904deSPeter Grehan IGP01IGC_PHY_PORT_CONFIG, 707517904deSPeter Grehan data); 708517904deSPeter Grehan if (ret_val) 709517904deSPeter Grehan return ret_val; 710517904deSPeter Grehan } 711517904deSPeter Grehan } else if ((phy->autoneg_advertised == IGC_ALL_SPEED_DUPLEX) || 712517904deSPeter Grehan (phy->autoneg_advertised == IGC_ALL_NOT_GIG) || 713517904deSPeter Grehan (phy->autoneg_advertised == IGC_ALL_10_SPEED)) { 714517904deSPeter Grehan data |= IGP02IGC_PM_D3_LPLU; 715517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, 716517904deSPeter Grehan data); 717517904deSPeter Grehan if (ret_val) 718517904deSPeter Grehan return ret_val; 719517904deSPeter Grehan 720517904deSPeter Grehan /* When LPLU is enabled, we should disable SmartSpeed */ 721517904deSPeter Grehan ret_val = phy->ops.read_reg(hw, IGP01IGC_PHY_PORT_CONFIG, 722517904deSPeter Grehan &data); 723517904deSPeter Grehan if (ret_val) 724517904deSPeter Grehan return ret_val; 725517904deSPeter Grehan 726517904deSPeter Grehan data &= ~IGP01IGC_PSCFR_SMART_SPEED; 727517904deSPeter Grehan ret_val = phy->ops.write_reg(hw, IGP01IGC_PHY_PORT_CONFIG, 728517904deSPeter Grehan data); 729517904deSPeter Grehan } 730517904deSPeter Grehan 731517904deSPeter Grehan return ret_val; 732517904deSPeter Grehan } 733517904deSPeter Grehan 734517904deSPeter Grehan /** 735517904deSPeter Grehan * igc_check_downshift_generic - Checks whether a downshift in speed occurred 736517904deSPeter Grehan * @hw: pointer to the HW structure 737517904deSPeter Grehan * 738517904deSPeter Grehan * Success returns 0, Failure returns 1 739517904deSPeter Grehan * 740517904deSPeter Grehan * A downshift is detected by querying the PHY link health. 741517904deSPeter Grehan **/ 742517904deSPeter Grehan s32 igc_check_downshift_generic(struct igc_hw *hw) 743517904deSPeter Grehan { 744517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 745517904deSPeter Grehan s32 ret_val; 746517904deSPeter Grehan 747517904deSPeter Grehan DEBUGFUNC("igc_check_downshift_generic"); 748517904deSPeter Grehan 749517904deSPeter Grehan switch (phy->type) { 750517904deSPeter Grehan case igc_phy_i225: 751517904deSPeter Grehan default: 752517904deSPeter Grehan /* speed downshift not supported */ 753517904deSPeter Grehan phy->speed_downgraded = false; 754517904deSPeter Grehan return IGC_SUCCESS; 755517904deSPeter Grehan } 756517904deSPeter Grehan 757517904deSPeter Grehan return ret_val; 758517904deSPeter Grehan } 759517904deSPeter Grehan 760517904deSPeter Grehan /** 761517904deSPeter Grehan * igc_wait_autoneg - Wait for auto-neg completion 762517904deSPeter Grehan * @hw: pointer to the HW structure 763517904deSPeter Grehan * 764517904deSPeter Grehan * Waits for auto-negotiation to complete or for the auto-negotiation time 765517904deSPeter Grehan * limit to expire, which ever happens first. 766517904deSPeter Grehan **/ 767517904deSPeter Grehan static s32 igc_wait_autoneg(struct igc_hw *hw) 768517904deSPeter Grehan { 769517904deSPeter Grehan s32 ret_val = IGC_SUCCESS; 770517904deSPeter Grehan u16 i, phy_status; 771517904deSPeter Grehan 772517904deSPeter Grehan DEBUGFUNC("igc_wait_autoneg"); 773517904deSPeter Grehan 774517904deSPeter Grehan if (!hw->phy.ops.read_reg) 775517904deSPeter Grehan return IGC_SUCCESS; 776517904deSPeter Grehan 777517904deSPeter Grehan /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ 778517904deSPeter Grehan for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { 779517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 780517904deSPeter Grehan if (ret_val) 781517904deSPeter Grehan break; 782517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 783517904deSPeter Grehan if (ret_val) 784517904deSPeter Grehan break; 785517904deSPeter Grehan if (phy_status & MII_SR_AUTONEG_COMPLETE) 786517904deSPeter Grehan break; 787517904deSPeter Grehan msec_delay(100); 788517904deSPeter Grehan } 789517904deSPeter Grehan 790517904deSPeter Grehan /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation 791517904deSPeter Grehan * has completed. 792517904deSPeter Grehan */ 793517904deSPeter Grehan return ret_val; 794517904deSPeter Grehan } 795517904deSPeter Grehan 796517904deSPeter Grehan /** 797517904deSPeter Grehan * igc_phy_has_link_generic - Polls PHY for link 798517904deSPeter Grehan * @hw: pointer to the HW structure 799517904deSPeter Grehan * @iterations: number of times to poll for link 800517904deSPeter Grehan * @usec_interval: delay between polling attempts 801517904deSPeter Grehan * @success: pointer to whether polling was successful or not 802517904deSPeter Grehan * 803517904deSPeter Grehan * Polls the PHY status register for link, 'iterations' number of times. 804517904deSPeter Grehan **/ 805517904deSPeter Grehan s32 igc_phy_has_link_generic(struct igc_hw *hw, u32 iterations, 806517904deSPeter Grehan u32 usec_interval, bool *success) 807517904deSPeter Grehan { 808517904deSPeter Grehan s32 ret_val = IGC_SUCCESS; 809517904deSPeter Grehan u16 i, phy_status; 810517904deSPeter Grehan 811517904deSPeter Grehan DEBUGFUNC("igc_phy_has_link_generic"); 812517904deSPeter Grehan 813517904deSPeter Grehan if (!hw->phy.ops.read_reg) 814517904deSPeter Grehan return IGC_SUCCESS; 815517904deSPeter Grehan 816517904deSPeter Grehan for (i = 0; i < iterations; i++) { 817517904deSPeter Grehan /* Some PHYs require the PHY_STATUS register to be read 818517904deSPeter Grehan * twice due to the link bit being sticky. No harm doing 819517904deSPeter Grehan * it across the board. 820517904deSPeter Grehan */ 821517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 822517904deSPeter Grehan if (ret_val) { 823517904deSPeter Grehan /* If the first read fails, another entity may have 824517904deSPeter Grehan * ownership of the resources, wait and try again to 825517904deSPeter Grehan * see if they have relinquished the resources yet. 826517904deSPeter Grehan */ 827517904deSPeter Grehan if (usec_interval >= 1000) 828517904deSPeter Grehan msec_delay(usec_interval/1000); 829517904deSPeter Grehan else 830517904deSPeter Grehan usec_delay(usec_interval); 831517904deSPeter Grehan } 832517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 833517904deSPeter Grehan if (ret_val) 834517904deSPeter Grehan break; 835517904deSPeter Grehan if (phy_status & MII_SR_LINK_STATUS) 836517904deSPeter Grehan break; 837517904deSPeter Grehan if (usec_interval >= 1000) 838517904deSPeter Grehan msec_delay(usec_interval/1000); 839517904deSPeter Grehan else 840517904deSPeter Grehan usec_delay(usec_interval); 841517904deSPeter Grehan } 842517904deSPeter Grehan 843517904deSPeter Grehan *success = (i < iterations); 844517904deSPeter Grehan 845517904deSPeter Grehan return ret_val; 846517904deSPeter Grehan } 847517904deSPeter Grehan 848517904deSPeter Grehan /** 849517904deSPeter Grehan * igc_phy_hw_reset_generic - PHY hardware reset 850517904deSPeter Grehan * @hw: pointer to the HW structure 851517904deSPeter Grehan * 852517904deSPeter Grehan * Verify the reset block is not blocking us from resetting. Acquire 853517904deSPeter Grehan * semaphore (if necessary) and read/set/write the device control reset 854517904deSPeter Grehan * bit in the PHY. Wait the appropriate delay time for the device to 855517904deSPeter Grehan * reset and release the semaphore (if necessary). 856517904deSPeter Grehan **/ 857517904deSPeter Grehan s32 igc_phy_hw_reset_generic(struct igc_hw *hw) 858517904deSPeter Grehan { 859517904deSPeter Grehan struct igc_phy_info *phy = &hw->phy; 860517904deSPeter Grehan s32 ret_val; 861517904deSPeter Grehan u32 ctrl, timeout = 10000, phpm = 0; 862517904deSPeter Grehan 863517904deSPeter Grehan DEBUGFUNC("igc_phy_hw_reset_generic"); 864517904deSPeter Grehan 865517904deSPeter Grehan if (phy->ops.check_reset_block) { 866517904deSPeter Grehan ret_val = phy->ops.check_reset_block(hw); 867517904deSPeter Grehan if (ret_val) 868517904deSPeter Grehan return IGC_SUCCESS; 869517904deSPeter Grehan } 870517904deSPeter Grehan 871517904deSPeter Grehan ret_val = phy->ops.acquire(hw); 872517904deSPeter Grehan if (ret_val) 873517904deSPeter Grehan return ret_val; 874517904deSPeter Grehan 875517904deSPeter Grehan phpm = IGC_READ_REG(hw, IGC_I225_PHPM); 876517904deSPeter Grehan 877517904deSPeter Grehan ctrl = IGC_READ_REG(hw, IGC_CTRL); 878517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl | IGC_CTRL_PHY_RST); 879517904deSPeter Grehan IGC_WRITE_FLUSH(hw); 880517904deSPeter Grehan 881517904deSPeter Grehan usec_delay(phy->reset_delay_us); 882517904deSPeter Grehan 883517904deSPeter Grehan IGC_WRITE_REG(hw, IGC_CTRL, ctrl); 884517904deSPeter Grehan IGC_WRITE_FLUSH(hw); 885517904deSPeter Grehan 886517904deSPeter Grehan usec_delay(150); 887517904deSPeter Grehan 888517904deSPeter Grehan do { 889517904deSPeter Grehan phpm = IGC_READ_REG(hw, IGC_I225_PHPM); 890517904deSPeter Grehan timeout--; 891517904deSPeter Grehan usec_delay(1); 892517904deSPeter Grehan } while (!(phpm & IGC_I225_PHPM_RST_COMPL) && timeout); 893517904deSPeter Grehan 894517904deSPeter Grehan if (!timeout) 895517904deSPeter Grehan DEBUGOUT("Timeout expired after a phy reset\n"); 896517904deSPeter Grehan 897517904deSPeter Grehan phy->ops.release(hw); 898517904deSPeter Grehan 899517904deSPeter Grehan return ret_val; 900517904deSPeter Grehan } 901517904deSPeter Grehan 902517904deSPeter Grehan /** 903517904deSPeter Grehan * igc_power_up_phy_copper - Restore copper link in case of PHY power down 904517904deSPeter Grehan * @hw: pointer to the HW structure 905517904deSPeter Grehan * 906517904deSPeter Grehan * In the case of a PHY power down to save power, or to turn off link during a 907517904deSPeter Grehan * driver unload, or wake on lan is not enabled, restore the link to previous 908517904deSPeter Grehan * settings. 909517904deSPeter Grehan **/ 910517904deSPeter Grehan void igc_power_up_phy_copper(struct igc_hw *hw) 911517904deSPeter Grehan { 912517904deSPeter Grehan u16 mii_reg = 0; 913517904deSPeter Grehan 914517904deSPeter Grehan /* The PHY will retain its settings across a power down/up cycle */ 915517904deSPeter Grehan hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 916517904deSPeter Grehan mii_reg &= ~MII_CR_POWER_DOWN; 917517904deSPeter Grehan hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 918517904deSPeter Grehan usec_delay(300); 919517904deSPeter Grehan } 920517904deSPeter Grehan 921517904deSPeter Grehan /** 922517904deSPeter Grehan * igc_power_down_phy_copper - Restore copper link in case of PHY power down 923517904deSPeter Grehan * @hw: pointer to the HW structure 924517904deSPeter Grehan * 925517904deSPeter Grehan * In the case of a PHY power down to save power, or to turn off link during a 926517904deSPeter Grehan * driver unload, or wake on lan is not enabled, restore the link to previous 927517904deSPeter Grehan * settings. 928517904deSPeter Grehan **/ 929517904deSPeter Grehan void igc_power_down_phy_copper(struct igc_hw *hw) 930517904deSPeter Grehan { 931517904deSPeter Grehan u16 mii_reg = 0; 932517904deSPeter Grehan 933517904deSPeter Grehan /* The PHY will retain its settings across a power down/up cycle */ 934517904deSPeter Grehan hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 935517904deSPeter Grehan mii_reg |= MII_CR_POWER_DOWN; 936517904deSPeter Grehan hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 937517904deSPeter Grehan msec_delay(1); 938517904deSPeter Grehan } 939517904deSPeter Grehan /** 940517904deSPeter Grehan * igc_write_phy_reg_gpy - Write GPY PHY register 941517904deSPeter Grehan * @hw: pointer to the HW structure 942517904deSPeter Grehan * @offset: register offset to write to 943517904deSPeter Grehan * @data: data to write at register offset 944517904deSPeter Grehan * 945517904deSPeter Grehan * Acquires semaphore, if necessary, then writes the data to PHY register 946517904deSPeter Grehan * at the offset. Release any acquired semaphores before exiting. 947517904deSPeter Grehan **/ 948517904deSPeter Grehan s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data) 949517904deSPeter Grehan { 950517904deSPeter Grehan s32 ret_val; 951517904deSPeter Grehan u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 952517904deSPeter Grehan 953517904deSPeter Grehan DEBUGFUNC("igc_write_phy_reg_gpy"); 954517904deSPeter Grehan 955517904deSPeter Grehan offset = offset & GPY_REG_MASK; 956517904deSPeter Grehan 957517904deSPeter Grehan if (!dev_addr) { 958517904deSPeter Grehan ret_val = hw->phy.ops.acquire(hw); 959517904deSPeter Grehan if (ret_val) 960517904deSPeter Grehan return ret_val; 961517904deSPeter Grehan ret_val = igc_write_phy_reg_mdic(hw, offset, data); 962517904deSPeter Grehan if (ret_val) 963517904deSPeter Grehan return ret_val; 964517904deSPeter Grehan hw->phy.ops.release(hw); 965517904deSPeter Grehan } else { 966517904deSPeter Grehan ret_val = igc_write_xmdio_reg(hw, (u16)offset, dev_addr, 967517904deSPeter Grehan data); 968517904deSPeter Grehan } 969517904deSPeter Grehan return ret_val; 970517904deSPeter Grehan } 971517904deSPeter Grehan 972517904deSPeter Grehan /** 973517904deSPeter Grehan * igc_read_phy_reg_gpy - Read GPY PHY register 974517904deSPeter Grehan * @hw: pointer to the HW structure 975517904deSPeter Grehan * @offset: lower half is register offset to read to 976517904deSPeter Grehan * upper half is MMD to use. 977517904deSPeter Grehan * @data: data to read at register offset 978517904deSPeter Grehan * 979517904deSPeter Grehan * Acquires semaphore, if necessary, then reads the data in the PHY register 980517904deSPeter Grehan * at the offset. Release any acquired semaphores before exiting. 981517904deSPeter Grehan **/ 982517904deSPeter Grehan s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data) 983517904deSPeter Grehan { 984517904deSPeter Grehan s32 ret_val; 985517904deSPeter Grehan u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 986517904deSPeter Grehan 987517904deSPeter Grehan DEBUGFUNC("igc_read_phy_reg_gpy"); 988517904deSPeter Grehan 989517904deSPeter Grehan offset = offset & GPY_REG_MASK; 990517904deSPeter Grehan 991517904deSPeter Grehan if (!dev_addr) { 992517904deSPeter Grehan ret_val = hw->phy.ops.acquire(hw); 993517904deSPeter Grehan if (ret_val) 994517904deSPeter Grehan return ret_val; 995517904deSPeter Grehan ret_val = igc_read_phy_reg_mdic(hw, offset, data); 996517904deSPeter Grehan if (ret_val) 997517904deSPeter Grehan return ret_val; 998517904deSPeter Grehan hw->phy.ops.release(hw); 999517904deSPeter Grehan } else { 1000517904deSPeter Grehan ret_val = igc_read_xmdio_reg(hw, (u16)offset, dev_addr, 1001517904deSPeter Grehan data); 1002517904deSPeter Grehan } 1003517904deSPeter Grehan return ret_val; 1004517904deSPeter Grehan } 1005517904deSPeter Grehan 1006517904deSPeter Grehan 1007517904deSPeter Grehan /** 1008517904deSPeter Grehan * __igc_access_xmdio_reg - Read/write XMDIO register 1009517904deSPeter Grehan * @hw: pointer to the HW structure 1010517904deSPeter Grehan * @address: XMDIO address to program 1011517904deSPeter Grehan * @dev_addr: device address to program 1012517904deSPeter Grehan * @data: pointer to value to read/write from/to the XMDIO address 1013517904deSPeter Grehan * @read: boolean flag to indicate read or write 1014517904deSPeter Grehan **/ 1015517904deSPeter Grehan static s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address, 1016517904deSPeter Grehan u8 dev_addr, u16 *data, bool read) 1017517904deSPeter Grehan { 1018517904deSPeter Grehan s32 ret_val; 1019517904deSPeter Grehan 1020517904deSPeter Grehan DEBUGFUNC("__igc_access_xmdio_reg"); 1021517904deSPeter Grehan 1022517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr); 1023517904deSPeter Grehan if (ret_val) 1024517904deSPeter Grehan return ret_val; 1025517904deSPeter Grehan 1026517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address); 1027517904deSPeter Grehan if (ret_val) 1028517904deSPeter Grehan return ret_val; 1029517904deSPeter Grehan 1030517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA | 1031517904deSPeter Grehan dev_addr); 1032517904deSPeter Grehan if (ret_val) 1033517904deSPeter Grehan return ret_val; 1034517904deSPeter Grehan 1035517904deSPeter Grehan if (read) 1036517904deSPeter Grehan ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data); 1037517904deSPeter Grehan else 1038517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data); 1039517904deSPeter Grehan if (ret_val) 1040517904deSPeter Grehan return ret_val; 1041517904deSPeter Grehan 1042517904deSPeter Grehan /* Recalibrate the device back to 0 */ 1043517904deSPeter Grehan ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0); 1044517904deSPeter Grehan if (ret_val) 1045517904deSPeter Grehan return ret_val; 1046517904deSPeter Grehan 1047517904deSPeter Grehan return ret_val; 1048517904deSPeter Grehan } 1049517904deSPeter Grehan 1050517904deSPeter Grehan /** 1051517904deSPeter Grehan * igc_read_xmdio_reg - Read XMDIO register 1052517904deSPeter Grehan * @hw: pointer to the HW structure 1053517904deSPeter Grehan * @addr: XMDIO address to program 1054517904deSPeter Grehan * @dev_addr: device address to program 1055517904deSPeter Grehan * @data: value to be read from the EMI address 1056517904deSPeter Grehan **/ 1057517904deSPeter Grehan s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 *data) 1058517904deSPeter Grehan { 1059517904deSPeter Grehan DEBUGFUNC("igc_read_xmdio_reg"); 1060517904deSPeter Grehan 1061517904deSPeter Grehan return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true); 1062517904deSPeter Grehan } 1063517904deSPeter Grehan 1064517904deSPeter Grehan /** 1065517904deSPeter Grehan * igc_write_xmdio_reg - Write XMDIO register 1066517904deSPeter Grehan * @hw: pointer to the HW structure 1067517904deSPeter Grehan * @addr: XMDIO address to program 1068517904deSPeter Grehan * @dev_addr: device address to program 1069517904deSPeter Grehan * @data: value to be written to the XMDIO address 1070517904deSPeter Grehan **/ 1071517904deSPeter Grehan s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 data) 1072517904deSPeter Grehan { 1073517904deSPeter Grehan DEBUGFUNC("igc_write_xmdio_reg"); 1074517904deSPeter Grehan 1075517904deSPeter Grehan return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false); 1076517904deSPeter Grehan } 1077