162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018 Intel Corporation */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "igc_phy.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * igc_check_reset_block - Check if PHY reset is blocked 862306a36Sopenharmony_ci * @hw: pointer to the HW structure 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Read the PHY management control register and check whether a PHY reset 1162306a36Sopenharmony_ci * is blocked. If a reset is not blocked return 0, otherwise 1262306a36Sopenharmony_ci * return IGC_ERR_BLK_PHY_RESET (12). 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_cis32 igc_check_reset_block(struct igc_hw *hw) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci u32 manc; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci manc = rd32(IGC_MANC); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci return (manc & IGC_MANC_BLK_PHY_RST_ON_IDE) ? 2162306a36Sopenharmony_ci IGC_ERR_BLK_PHY_RESET : 0; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * igc_get_phy_id - Retrieve the PHY ID and revision 2662306a36Sopenharmony_ci * @hw: pointer to the HW structure 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Reads the PHY registers and stores the PHY ID and possibly the PHY 2962306a36Sopenharmony_ci * revision in the hardware structure. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cis32 igc_get_phy_id(struct igc_hw *hw) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 3462306a36Sopenharmony_ci s32 ret_val = 0; 3562306a36Sopenharmony_ci u16 phy_id; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); 3862306a36Sopenharmony_ci if (ret_val) 3962306a36Sopenharmony_ci goto out; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci phy->id = (u32)(phy_id << 16); 4262306a36Sopenharmony_ci usleep_range(200, 500); 4362306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); 4462306a36Sopenharmony_ci if (ret_val) 4562306a36Sopenharmony_ci goto out; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci phy->id |= (u32)(phy_id & PHY_REVISION_MASK); 4862306a36Sopenharmony_ci phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciout: 5162306a36Sopenharmony_ci return ret_val; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * igc_phy_has_link - Polls PHY for link 5662306a36Sopenharmony_ci * @hw: pointer to the HW structure 5762306a36Sopenharmony_ci * @iterations: number of times to poll for link 5862306a36Sopenharmony_ci * @usec_interval: delay between polling attempts 5962306a36Sopenharmony_ci * @success: pointer to whether polling was successful or not 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Polls the PHY status register for link, 'iterations' number of times. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cis32 igc_phy_has_link(struct igc_hw *hw, u32 iterations, 6462306a36Sopenharmony_ci u32 usec_interval, bool *success) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci u16 i, phy_status; 6762306a36Sopenharmony_ci s32 ret_val = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci for (i = 0; i < iterations; i++) { 7062306a36Sopenharmony_ci /* Some PHYs require the PHY_STATUS register to be read 7162306a36Sopenharmony_ci * twice due to the link bit being sticky. No harm doing 7262306a36Sopenharmony_ci * it across the board. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 7562306a36Sopenharmony_ci if (ret_val && usec_interval > 0) { 7662306a36Sopenharmony_ci /* If the first read fails, another entity may have 7762306a36Sopenharmony_ci * ownership of the resources, wait and try again to 7862306a36Sopenharmony_ci * see if they have relinquished the resources yet. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci if (usec_interval >= 1000) 8162306a36Sopenharmony_ci mdelay(usec_interval / 1000); 8262306a36Sopenharmony_ci else 8362306a36Sopenharmony_ci udelay(usec_interval); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 8662306a36Sopenharmony_ci if (ret_val) 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci if (phy_status & MII_SR_LINK_STATUS) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci if (usec_interval >= 1000) 9162306a36Sopenharmony_ci mdelay(usec_interval / 1000); 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci udelay(usec_interval); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci *success = (i < iterations) ? true : false; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return ret_val; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * igc_power_up_phy_copper - Restore copper link in case of PHY power down 10362306a36Sopenharmony_ci * @hw: pointer to the HW structure 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * In the case of a PHY power down to save power, or to turn off link during a 10662306a36Sopenharmony_ci * driver unload, restore the link to previous settings. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_civoid igc_power_up_phy_copper(struct igc_hw *hw) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u16 mii_reg = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* The PHY will retain its settings across a power down/up cycle */ 11362306a36Sopenharmony_ci hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 11462306a36Sopenharmony_ci mii_reg &= ~MII_CR_POWER_DOWN; 11562306a36Sopenharmony_ci hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/** 11962306a36Sopenharmony_ci * igc_power_down_phy_copper - Power down copper PHY 12062306a36Sopenharmony_ci * @hw: pointer to the HW structure 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * Power down PHY to save power when interface is down and wake on lan 12362306a36Sopenharmony_ci * is not enabled. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_civoid igc_power_down_phy_copper(struct igc_hw *hw) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci u16 mii_reg = 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* The PHY will retain its settings across a power down/up cycle */ 13062306a36Sopenharmony_ci hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 13162306a36Sopenharmony_ci mii_reg |= MII_CR_POWER_DOWN; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Temporary workaround - should be removed when PHY will implement 13462306a36Sopenharmony_ci * IEEE registers as properly 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci /* hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);*/ 13762306a36Sopenharmony_ci usleep_range(1000, 2000); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * igc_check_downshift - Checks whether a downshift in speed occurred 14262306a36Sopenharmony_ci * @hw: pointer to the HW structure 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * A downshift is detected by querying the PHY link health. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_civoid igc_check_downshift(struct igc_hw *hw) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* speed downshift not supported */ 15162306a36Sopenharmony_ci phy->speed_downgraded = false; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * igc_phy_hw_reset - PHY hardware reset 15662306a36Sopenharmony_ci * @hw: pointer to the HW structure 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Verify the reset block is not blocking us from resetting. Acquire 15962306a36Sopenharmony_ci * semaphore (if necessary) and read/set/write the device control reset 16062306a36Sopenharmony_ci * bit in the PHY. Wait the appropriate delay time for the device to 16162306a36Sopenharmony_ci * reset and release the semaphore (if necessary). 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cis32 igc_phy_hw_reset(struct igc_hw *hw) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 16662306a36Sopenharmony_ci u32 phpm = 0, timeout = 10000; 16762306a36Sopenharmony_ci s32 ret_val; 16862306a36Sopenharmony_ci u32 ctrl; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ret_val = igc_check_reset_block(hw); 17162306a36Sopenharmony_ci if (ret_val) { 17262306a36Sopenharmony_ci ret_val = 0; 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret_val = phy->ops.acquire(hw); 17762306a36Sopenharmony_ci if (ret_val) 17862306a36Sopenharmony_ci goto out; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci phpm = rd32(IGC_I225_PHPM); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ctrl = rd32(IGC_CTRL); 18362306a36Sopenharmony_ci wr32(IGC_CTRL, ctrl | IGC_CTRL_PHY_RST); 18462306a36Sopenharmony_ci wrfl(); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci udelay(phy->reset_delay_us); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci wr32(IGC_CTRL, ctrl); 18962306a36Sopenharmony_ci wrfl(); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* SW should guarantee 100us for the completion of the PHY reset */ 19262306a36Sopenharmony_ci usleep_range(100, 150); 19362306a36Sopenharmony_ci do { 19462306a36Sopenharmony_ci phpm = rd32(IGC_I225_PHPM); 19562306a36Sopenharmony_ci timeout--; 19662306a36Sopenharmony_ci udelay(1); 19762306a36Sopenharmony_ci } while (!(phpm & IGC_PHY_RST_COMP) && timeout); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!timeout) 20062306a36Sopenharmony_ci hw_dbg("Timeout is expired after a phy reset\n"); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci usleep_range(100, 150); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci phy->ops.release(hw); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciout: 20762306a36Sopenharmony_ci return ret_val; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/** 21162306a36Sopenharmony_ci * igc_phy_setup_autoneg - Configure PHY for auto-negotiation 21262306a36Sopenharmony_ci * @hw: pointer to the HW structure 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Reads the MII auto-neg advertisement register and/or the 1000T control 21562306a36Sopenharmony_ci * register and if the PHY is already setup for auto-negotiation, then 21662306a36Sopenharmony_ci * return successful. Otherwise, setup advertisement and flow control to 21762306a36Sopenharmony_ci * the appropriate values for the wanted auto-negotiation. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_cistatic s32 igc_phy_setup_autoneg(struct igc_hw *hw) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 22262306a36Sopenharmony_ci u16 aneg_multigbt_an_ctrl = 0; 22362306a36Sopenharmony_ci u16 mii_1000t_ctrl_reg = 0; 22462306a36Sopenharmony_ci u16 mii_autoneg_adv_reg; 22562306a36Sopenharmony_ci s32 ret_val; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci phy->autoneg_advertised &= phy->autoneg_mask; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Read the MII Auto-Neg Advertisement Register (Address 4). */ 23062306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); 23162306a36Sopenharmony_ci if (ret_val) 23262306a36Sopenharmony_ci return ret_val; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_1000_FULL) { 23562306a36Sopenharmony_ci /* Read the MII 1000Base-T Control Register (Address 9). */ 23662306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, 23762306a36Sopenharmony_ci &mii_1000t_ctrl_reg); 23862306a36Sopenharmony_ci if (ret_val) 23962306a36Sopenharmony_ci return ret_val; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_2500_FULL) { 24362306a36Sopenharmony_ci /* Read the MULTI GBT AN Control Register - reg 7.32 */ 24462306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK << 24562306a36Sopenharmony_ci MMD_DEVADDR_SHIFT) | 24662306a36Sopenharmony_ci ANEG_MULTIGBT_AN_CTRL, 24762306a36Sopenharmony_ci &aneg_multigbt_an_ctrl); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ret_val) 25062306a36Sopenharmony_ci return ret_val; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Need to parse both autoneg_advertised and fc and set up 25462306a36Sopenharmony_ci * the appropriate PHY registers. First we will parse for 25562306a36Sopenharmony_ci * autoneg_advertised software override. Since we can advertise 25662306a36Sopenharmony_ci * a plethora of combinations, we need to check each bit 25762306a36Sopenharmony_ci * individually. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* First we clear all the 10/100 mb speed bits in the Auto-Neg 26162306a36Sopenharmony_ci * Advertisement Register (Address 4) and the 1000 mb speed bits in 26262306a36Sopenharmony_ci * the 1000Base-T Control Register (Address 9). 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | 26562306a36Sopenharmony_ci NWAY_AR_100TX_HD_CAPS | 26662306a36Sopenharmony_ci NWAY_AR_10T_FD_CAPS | 26762306a36Sopenharmony_ci NWAY_AR_10T_HD_CAPS); 26862306a36Sopenharmony_ci mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci hw_dbg("autoneg_advertised %x\n", phy->autoneg_advertised); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Do we want to advertise 10 Mb Half Duplex? */ 27362306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_10_HALF) { 27462306a36Sopenharmony_ci hw_dbg("Advertise 10mb Half duplex\n"); 27562306a36Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Do we want to advertise 10 Mb Full Duplex? */ 27962306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_10_FULL) { 28062306a36Sopenharmony_ci hw_dbg("Advertise 10mb Full duplex\n"); 28162306a36Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Do we want to advertise 100 Mb Half Duplex? */ 28562306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_100_HALF) { 28662306a36Sopenharmony_ci hw_dbg("Advertise 100mb Half duplex\n"); 28762306a36Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Do we want to advertise 100 Mb Full Duplex? */ 29162306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_100_FULL) { 29262306a36Sopenharmony_ci hw_dbg("Advertise 100mb Full duplex\n"); 29362306a36Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ 29762306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_1000_HALF) 29862306a36Sopenharmony_ci hw_dbg("Advertise 1000mb Half duplex request denied!\n"); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Do we want to advertise 1000 Mb Full Duplex? */ 30162306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { 30262306a36Sopenharmony_ci hw_dbg("Advertise 1000mb Full duplex\n"); 30362306a36Sopenharmony_ci mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* We do not allow the Phy to advertise 2500 Mb Half Duplex */ 30762306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_2500_HALF) 30862306a36Sopenharmony_ci hw_dbg("Advertise 2500mb Half duplex request denied!\n"); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Do we want to advertise 2500 Mb Full Duplex? */ 31162306a36Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_2500_FULL) { 31262306a36Sopenharmony_ci hw_dbg("Advertise 2500mb Full duplex\n"); 31362306a36Sopenharmony_ci aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS; 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Check for a software override of the flow control settings, and 31962306a36Sopenharmony_ci * setup the PHY advertisement registers accordingly. If 32062306a36Sopenharmony_ci * auto-negotiation is enabled, then software will have to set the 32162306a36Sopenharmony_ci * "PAUSE" bits to the correct value in the Auto-Negotiation 32262306a36Sopenharmony_ci * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- 32362306a36Sopenharmony_ci * negotiation. 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * The possible values of the "fc" parameter are: 32662306a36Sopenharmony_ci * 0: Flow control is completely disabled 32762306a36Sopenharmony_ci * 1: Rx flow control is enabled (we can receive pause frames 32862306a36Sopenharmony_ci * but not send pause frames). 32962306a36Sopenharmony_ci * 2: Tx flow control is enabled (we can send pause frames 33062306a36Sopenharmony_ci * but we do not support receiving pause frames). 33162306a36Sopenharmony_ci * 3: Both Rx and Tx flow control (symmetric) are enabled. 33262306a36Sopenharmony_ci * other: No software override. The flow control configuration 33362306a36Sopenharmony_ci * in the EEPROM is used. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci switch (hw->fc.current_mode) { 33662306a36Sopenharmony_ci case igc_fc_none: 33762306a36Sopenharmony_ci /* Flow control (Rx & Tx) is completely disabled by a 33862306a36Sopenharmony_ci * software over-ride. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case igc_fc_rx_pause: 34362306a36Sopenharmony_ci /* Rx Flow control is enabled, and Tx Flow control is 34462306a36Sopenharmony_ci * disabled, by a software over-ride. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * Since there really isn't a way to advertise that we are 34762306a36Sopenharmony_ci * capable of Rx Pause ONLY, we will advertise that we 34862306a36Sopenharmony_ci * support both symmetric and asymmetric Rx PAUSE. Later 34962306a36Sopenharmony_ci * (in igc_config_fc_after_link_up) we will disable the 35062306a36Sopenharmony_ci * hw's ability to send PAUSE frames. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case igc_fc_tx_pause: 35562306a36Sopenharmony_ci /* Tx Flow control is enabled, and Rx Flow control is 35662306a36Sopenharmony_ci * disabled, by a software over-ride. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; 35962306a36Sopenharmony_ci mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci case igc_fc_full: 36262306a36Sopenharmony_ci /* Flow control (both Rx and Tx) is enabled by a software 36362306a36Sopenharmony_ci * over-ride. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci default: 36862306a36Sopenharmony_ci hw_dbg("Flow control param set incorrectly\n"); 36962306a36Sopenharmony_ci return -IGC_ERR_CONFIG; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); 37362306a36Sopenharmony_ci if (ret_val) 37462306a36Sopenharmony_ci return ret_val; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci hw_dbg("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_1000_FULL) 37962306a36Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, 38062306a36Sopenharmony_ci mii_1000t_ctrl_reg); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_2500_FULL) 38362306a36Sopenharmony_ci ret_val = phy->ops.write_reg(hw, 38462306a36Sopenharmony_ci (STANDARD_AN_REG_MASK << 38562306a36Sopenharmony_ci MMD_DEVADDR_SHIFT) | 38662306a36Sopenharmony_ci ANEG_MULTIGBT_AN_CTRL, 38762306a36Sopenharmony_ci aneg_multigbt_an_ctrl); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return ret_val; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * igc_wait_autoneg - Wait for auto-neg completion 39462306a36Sopenharmony_ci * @hw: pointer to the HW structure 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * Waits for auto-negotiation to complete or for the auto-negotiation time 39762306a36Sopenharmony_ci * limit to expire, which ever happens first. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic s32 igc_wait_autoneg(struct igc_hw *hw) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci u16 i, phy_status; 40262306a36Sopenharmony_ci s32 ret_val = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ 40562306a36Sopenharmony_ci for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { 40662306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 40762306a36Sopenharmony_ci if (ret_val) 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 41062306a36Sopenharmony_ci if (ret_val) 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci if (phy_status & MII_SR_AUTONEG_COMPLETE) 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci msleep(100); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation 41862306a36Sopenharmony_ci * has completed. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci return ret_val; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/** 42462306a36Sopenharmony_ci * igc_copper_link_autoneg - Setup/Enable autoneg for copper link 42562306a36Sopenharmony_ci * @hw: pointer to the HW structure 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * Performs initial bounds checking on autoneg advertisement parameter, then 42862306a36Sopenharmony_ci * configure to advertise the full capability. Setup the PHY to autoneg 42962306a36Sopenharmony_ci * and restart the negotiation process between the link partner. If 43062306a36Sopenharmony_ci * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic s32 igc_copper_link_autoneg(struct igc_hw *hw) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 43562306a36Sopenharmony_ci u16 phy_ctrl; 43662306a36Sopenharmony_ci s32 ret_val; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Perform some bounds checking on the autoneg advertisement 43962306a36Sopenharmony_ci * parameter. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci phy->autoneg_advertised &= phy->autoneg_mask; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* If autoneg_advertised is zero, we assume it was not defaulted 44462306a36Sopenharmony_ci * by the calling code so we set to advertise full capability. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci if (phy->autoneg_advertised == 0) 44762306a36Sopenharmony_ci phy->autoneg_advertised = phy->autoneg_mask; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci hw_dbg("Reconfiguring auto-neg advertisement params\n"); 45062306a36Sopenharmony_ci ret_val = igc_phy_setup_autoneg(hw); 45162306a36Sopenharmony_ci if (ret_val) { 45262306a36Sopenharmony_ci hw_dbg("Error Setting up Auto-Negotiation\n"); 45362306a36Sopenharmony_ci goto out; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci hw_dbg("Restarting Auto-Neg\n"); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Restart auto-negotiation by setting the Auto Neg Enable bit and 45862306a36Sopenharmony_ci * the Auto Neg Restart bit in the PHY control register. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); 46162306a36Sopenharmony_ci if (ret_val) 46262306a36Sopenharmony_ci goto out; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); 46562306a36Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); 46662306a36Sopenharmony_ci if (ret_val) 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Does the user want to wait for Auto-Neg to complete here, or 47062306a36Sopenharmony_ci * check at a later time (for example, callback routine). 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (phy->autoneg_wait_to_complete) { 47362306a36Sopenharmony_ci ret_val = igc_wait_autoneg(hw); 47462306a36Sopenharmony_ci if (ret_val) { 47562306a36Sopenharmony_ci hw_dbg("Error while waiting for autoneg to complete\n"); 47662306a36Sopenharmony_ci goto out; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci hw->mac.get_link_status = true; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciout: 48362306a36Sopenharmony_ci return ret_val; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * igc_setup_copper_link - Configure copper link settings 48862306a36Sopenharmony_ci * @hw: pointer to the HW structure 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Calls the appropriate function to configure the link for auto-neg or forced 49162306a36Sopenharmony_ci * speed and duplex. Then we check for link, once link is established calls 49262306a36Sopenharmony_ci * to configure collision distance and flow control are called. If link is 49362306a36Sopenharmony_ci * not established, we return -IGC_ERR_PHY (-2). 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cis32 igc_setup_copper_link(struct igc_hw *hw) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci s32 ret_val = 0; 49862306a36Sopenharmony_ci bool link; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (hw->mac.autoneg) { 50162306a36Sopenharmony_ci /* Setup autoneg and flow control advertisement and perform 50262306a36Sopenharmony_ci * autonegotiation. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci ret_val = igc_copper_link_autoneg(hw); 50562306a36Sopenharmony_ci if (ret_val) 50662306a36Sopenharmony_ci goto out; 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci /* PHY will be set to 10H, 10F, 100H or 100F 50962306a36Sopenharmony_ci * depending on user settings. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci hw_dbg("Forcing Speed and Duplex\n"); 51262306a36Sopenharmony_ci ret_val = hw->phy.ops.force_speed_duplex(hw); 51362306a36Sopenharmony_ci if (ret_val) { 51462306a36Sopenharmony_ci hw_dbg("Error Forcing Speed and Duplex\n"); 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Check link status. Wait up to 100 microseconds for link to become 52062306a36Sopenharmony_ci * valid. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci ret_val = igc_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link); 52362306a36Sopenharmony_ci if (ret_val) 52462306a36Sopenharmony_ci goto out; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (link) { 52762306a36Sopenharmony_ci hw_dbg("Valid link established!!!\n"); 52862306a36Sopenharmony_ci igc_config_collision_dist(hw); 52962306a36Sopenharmony_ci ret_val = igc_config_fc_after_link_up(hw); 53062306a36Sopenharmony_ci } else { 53162306a36Sopenharmony_ci hw_dbg("Unable to establish link!!!\n"); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciout: 53562306a36Sopenharmony_ci return ret_val; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/** 53962306a36Sopenharmony_ci * igc_read_phy_reg_mdic - Read MDI control register 54062306a36Sopenharmony_ci * @hw: pointer to the HW structure 54162306a36Sopenharmony_ci * @offset: register offset to be read 54262306a36Sopenharmony_ci * @data: pointer to the read data 54362306a36Sopenharmony_ci * 54462306a36Sopenharmony_ci * Reads the MDI control register in the PHY at offset and stores the 54562306a36Sopenharmony_ci * information read to data. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_cistatic s32 igc_read_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 *data) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 55062306a36Sopenharmony_ci u32 i, mdic = 0; 55162306a36Sopenharmony_ci s32 ret_val = 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (offset > MAX_PHY_REG_ADDRESS) { 55462306a36Sopenharmony_ci hw_dbg("PHY Address %d is out of range\n", offset); 55562306a36Sopenharmony_ci ret_val = -IGC_ERR_PARAM; 55662306a36Sopenharmony_ci goto out; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Set up Op-code, Phy Address, and register offset in the MDI 56062306a36Sopenharmony_ci * Control register. The MAC will take care of interfacing with the 56162306a36Sopenharmony_ci * PHY to retrieve the desired data. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci mdic = ((offset << IGC_MDIC_REG_SHIFT) | 56462306a36Sopenharmony_ci (phy->addr << IGC_MDIC_PHY_SHIFT) | 56562306a36Sopenharmony_ci (IGC_MDIC_OP_READ)); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci wr32(IGC_MDIC, mdic); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Poll the ready bit to see if the MDI read completed 57062306a36Sopenharmony_ci * Increasing the time out as testing showed failures with 57162306a36Sopenharmony_ci * the lower time out 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci for (i = 0; i < IGC_GEN_POLL_TIMEOUT; i++) { 57462306a36Sopenharmony_ci udelay(50); 57562306a36Sopenharmony_ci mdic = rd32(IGC_MDIC); 57662306a36Sopenharmony_ci if (mdic & IGC_MDIC_READY) 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci if (!(mdic & IGC_MDIC_READY)) { 58062306a36Sopenharmony_ci hw_dbg("MDI Read did not complete\n"); 58162306a36Sopenharmony_ci ret_val = -IGC_ERR_PHY; 58262306a36Sopenharmony_ci goto out; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci if (mdic & IGC_MDIC_ERROR) { 58562306a36Sopenharmony_ci hw_dbg("MDI Error\n"); 58662306a36Sopenharmony_ci ret_val = -IGC_ERR_PHY; 58762306a36Sopenharmony_ci goto out; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci *data = (u16)mdic; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciout: 59262306a36Sopenharmony_ci return ret_val; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/** 59662306a36Sopenharmony_ci * igc_write_phy_reg_mdic - Write MDI control register 59762306a36Sopenharmony_ci * @hw: pointer to the HW structure 59862306a36Sopenharmony_ci * @offset: register offset to write to 59962306a36Sopenharmony_ci * @data: data to write to register at offset 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * Writes data to MDI control register in the PHY at offset. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_cistatic s32 igc_write_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 data) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 60662306a36Sopenharmony_ci u32 i, mdic = 0; 60762306a36Sopenharmony_ci s32 ret_val = 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (offset > MAX_PHY_REG_ADDRESS) { 61062306a36Sopenharmony_ci hw_dbg("PHY Address %d is out of range\n", offset); 61162306a36Sopenharmony_ci ret_val = -IGC_ERR_PARAM; 61262306a36Sopenharmony_ci goto out; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Set up Op-code, Phy Address, and register offset in the MDI 61662306a36Sopenharmony_ci * Control register. The MAC will take care of interfacing with the 61762306a36Sopenharmony_ci * PHY to write the desired data. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci mdic = (((u32)data) | 62062306a36Sopenharmony_ci (offset << IGC_MDIC_REG_SHIFT) | 62162306a36Sopenharmony_ci (phy->addr << IGC_MDIC_PHY_SHIFT) | 62262306a36Sopenharmony_ci (IGC_MDIC_OP_WRITE)); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci wr32(IGC_MDIC, mdic); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Poll the ready bit to see if the MDI read completed 62762306a36Sopenharmony_ci * Increasing the time out as testing showed failures with 62862306a36Sopenharmony_ci * the lower time out 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci for (i = 0; i < IGC_GEN_POLL_TIMEOUT; i++) { 63162306a36Sopenharmony_ci udelay(50); 63262306a36Sopenharmony_ci mdic = rd32(IGC_MDIC); 63362306a36Sopenharmony_ci if (mdic & IGC_MDIC_READY) 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci if (!(mdic & IGC_MDIC_READY)) { 63762306a36Sopenharmony_ci hw_dbg("MDI Write did not complete\n"); 63862306a36Sopenharmony_ci ret_val = -IGC_ERR_PHY; 63962306a36Sopenharmony_ci goto out; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci if (mdic & IGC_MDIC_ERROR) { 64262306a36Sopenharmony_ci hw_dbg("MDI Error\n"); 64362306a36Sopenharmony_ci ret_val = -IGC_ERR_PHY; 64462306a36Sopenharmony_ci goto out; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciout: 64862306a36Sopenharmony_ci return ret_val; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/** 65262306a36Sopenharmony_ci * __igc_access_xmdio_reg - Read/write XMDIO register 65362306a36Sopenharmony_ci * @hw: pointer to the HW structure 65462306a36Sopenharmony_ci * @address: XMDIO address to program 65562306a36Sopenharmony_ci * @dev_addr: device address to program 65662306a36Sopenharmony_ci * @data: pointer to value to read/write from/to the XMDIO address 65762306a36Sopenharmony_ci * @read: boolean flag to indicate read or write 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_cistatic s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address, 66062306a36Sopenharmony_ci u8 dev_addr, u16 *data, bool read) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci s32 ret_val; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr); 66562306a36Sopenharmony_ci if (ret_val) 66662306a36Sopenharmony_ci return ret_val; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address); 66962306a36Sopenharmony_ci if (ret_val) 67062306a36Sopenharmony_ci return ret_val; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA | 67362306a36Sopenharmony_ci dev_addr); 67462306a36Sopenharmony_ci if (ret_val) 67562306a36Sopenharmony_ci return ret_val; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (read) 67862306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data); 67962306a36Sopenharmony_ci else 68062306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data); 68162306a36Sopenharmony_ci if (ret_val) 68262306a36Sopenharmony_ci return ret_val; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Recalibrate the device back to 0 */ 68562306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0); 68662306a36Sopenharmony_ci if (ret_val) 68762306a36Sopenharmony_ci return ret_val; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return ret_val; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/** 69362306a36Sopenharmony_ci * igc_read_xmdio_reg - Read XMDIO register 69462306a36Sopenharmony_ci * @hw: pointer to the HW structure 69562306a36Sopenharmony_ci * @addr: XMDIO address to program 69662306a36Sopenharmony_ci * @dev_addr: device address to program 69762306a36Sopenharmony_ci * @data: value to be read from the EMI address 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_cistatic s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, 70062306a36Sopenharmony_ci u8 dev_addr, u16 *data) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true); 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/** 70662306a36Sopenharmony_ci * igc_write_xmdio_reg - Write XMDIO register 70762306a36Sopenharmony_ci * @hw: pointer to the HW structure 70862306a36Sopenharmony_ci * @addr: XMDIO address to program 70962306a36Sopenharmony_ci * @dev_addr: device address to program 71062306a36Sopenharmony_ci * @data: value to be written to the XMDIO address 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_cistatic s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, 71362306a36Sopenharmony_ci u8 dev_addr, u16 data) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/** 71962306a36Sopenharmony_ci * igc_write_phy_reg_gpy - Write GPY PHY register 72062306a36Sopenharmony_ci * @hw: pointer to the HW structure 72162306a36Sopenharmony_ci * @offset: register offset to write to 72262306a36Sopenharmony_ci * @data: data to write at register offset 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Acquires semaphore, if necessary, then writes the data to PHY register 72562306a36Sopenharmony_ci * at the offset. Release any acquired semaphores before exiting. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_cis32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 73062306a36Sopenharmony_ci s32 ret_val; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci offset = offset & GPY_REG_MASK; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!dev_addr) { 73562306a36Sopenharmony_ci ret_val = hw->phy.ops.acquire(hw); 73662306a36Sopenharmony_ci if (ret_val) 73762306a36Sopenharmony_ci return ret_val; 73862306a36Sopenharmony_ci ret_val = igc_write_phy_reg_mdic(hw, offset, data); 73962306a36Sopenharmony_ci hw->phy.ops.release(hw); 74062306a36Sopenharmony_ci } else { 74162306a36Sopenharmony_ci ret_val = igc_write_xmdio_reg(hw, (u16)offset, dev_addr, 74262306a36Sopenharmony_ci data); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return ret_val; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * igc_read_phy_reg_gpy - Read GPY PHY register 75062306a36Sopenharmony_ci * @hw: pointer to the HW structure 75162306a36Sopenharmony_ci * @offset: lower half is register offset to read to 75262306a36Sopenharmony_ci * upper half is MMD to use. 75362306a36Sopenharmony_ci * @data: data to read at register offset 75462306a36Sopenharmony_ci * 75562306a36Sopenharmony_ci * Acquires semaphore, if necessary, then reads the data in the PHY register 75662306a36Sopenharmony_ci * at the offset. Release any acquired semaphores before exiting. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_cis32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 76162306a36Sopenharmony_ci s32 ret_val; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci offset = offset & GPY_REG_MASK; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!dev_addr) { 76662306a36Sopenharmony_ci ret_val = hw->phy.ops.acquire(hw); 76762306a36Sopenharmony_ci if (ret_val) 76862306a36Sopenharmony_ci return ret_val; 76962306a36Sopenharmony_ci ret_val = igc_read_phy_reg_mdic(hw, offset, data); 77062306a36Sopenharmony_ci hw->phy.ops.release(hw); 77162306a36Sopenharmony_ci } else { 77262306a36Sopenharmony_ci ret_val = igc_read_xmdio_reg(hw, (u16)offset, dev_addr, 77362306a36Sopenharmony_ci data); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return ret_val; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/** 78062306a36Sopenharmony_ci * igc_read_phy_fw_version - Read gPHY firmware version 78162306a36Sopenharmony_ci * @hw: pointer to the HW structure 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ciu16 igc_read_phy_fw_version(struct igc_hw *hw) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 78662306a36Sopenharmony_ci u16 gphy_version = 0; 78762306a36Sopenharmony_ci u16 ret_val; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* NVM image version is reported as firmware version for i225 device */ 79062306a36Sopenharmony_ci ret_val = phy->ops.read_reg(hw, IGC_GPHY_VERSION, &gphy_version); 79162306a36Sopenharmony_ci if (ret_val) 79262306a36Sopenharmony_ci hw_dbg("igc_phy: read wrong gphy version\n"); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return gphy_version; 79562306a36Sopenharmony_ci} 796