18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018 Intel Corporation */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "igc_phy.h" 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * igc_check_reset_block - Check if PHY reset is blocked 88c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Read the PHY management control register and check whether a PHY reset 118c2ecf20Sopenharmony_ci * is blocked. If a reset is not blocked return 0, otherwise 128c2ecf20Sopenharmony_ci * return IGC_ERR_BLK_PHY_RESET (12). 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_cis32 igc_check_reset_block(struct igc_hw *hw) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci u32 manc; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci manc = rd32(IGC_MANC); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci return (manc & IGC_MANC_BLK_PHY_RST_ON_IDE) ? 218c2ecf20Sopenharmony_ci IGC_ERR_BLK_PHY_RESET : 0; 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * igc_get_phy_id - Retrieve the PHY ID and revision 268c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Reads the PHY registers and stores the PHY ID and possibly the PHY 298c2ecf20Sopenharmony_ci * revision in the hardware structure. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cis32 igc_get_phy_id(struct igc_hw *hw) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 348c2ecf20Sopenharmony_ci s32 ret_val = 0; 358c2ecf20Sopenharmony_ci u16 phy_id; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); 388c2ecf20Sopenharmony_ci if (ret_val) 398c2ecf20Sopenharmony_ci goto out; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci phy->id = (u32)(phy_id << 16); 428c2ecf20Sopenharmony_ci usleep_range(200, 500); 438c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); 448c2ecf20Sopenharmony_ci if (ret_val) 458c2ecf20Sopenharmony_ci goto out; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci phy->id |= (u32)(phy_id & PHY_REVISION_MASK); 488c2ecf20Sopenharmony_ci phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciout: 518c2ecf20Sopenharmony_ci return ret_val; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * igc_phy_has_link - Polls PHY for link 568c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 578c2ecf20Sopenharmony_ci * @iterations: number of times to poll for link 588c2ecf20Sopenharmony_ci * @usec_interval: delay between polling attempts 598c2ecf20Sopenharmony_ci * @success: pointer to whether polling was successful or not 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Polls the PHY status register for link, 'iterations' number of times. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cis32 igc_phy_has_link(struct igc_hw *hw, u32 iterations, 648c2ecf20Sopenharmony_ci u32 usec_interval, bool *success) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci u16 i, phy_status; 678c2ecf20Sopenharmony_ci s32 ret_val = 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < iterations; i++) { 708c2ecf20Sopenharmony_ci /* Some PHYs require the PHY_STATUS register to be read 718c2ecf20Sopenharmony_ci * twice due to the link bit being sticky. No harm doing 728c2ecf20Sopenharmony_ci * it across the board. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 758c2ecf20Sopenharmony_ci if (ret_val && usec_interval > 0) { 768c2ecf20Sopenharmony_ci /* If the first read fails, another entity may have 778c2ecf20Sopenharmony_ci * ownership of the resources, wait and try again to 788c2ecf20Sopenharmony_ci * see if they have relinquished the resources yet. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci if (usec_interval >= 1000) 818c2ecf20Sopenharmony_ci mdelay(usec_interval / 1000); 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci udelay(usec_interval); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 868c2ecf20Sopenharmony_ci if (ret_val) 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci if (phy_status & MII_SR_LINK_STATUS) 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci if (usec_interval >= 1000) 918c2ecf20Sopenharmony_ci mdelay(usec_interval / 1000); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci udelay(usec_interval); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci *success = (i < iterations) ? true : false; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return ret_val; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * igc_power_up_phy_copper - Restore copper link in case of PHY power down 1038c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * In the case of a PHY power down to save power, or to turn off link during a 1068c2ecf20Sopenharmony_ci * driver unload, restore the link to previous settings. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_civoid igc_power_up_phy_copper(struct igc_hw *hw) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u16 mii_reg = 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* The PHY will retain its settings across a power down/up cycle */ 1138c2ecf20Sopenharmony_ci hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 1148c2ecf20Sopenharmony_ci mii_reg &= ~MII_CR_POWER_DOWN; 1158c2ecf20Sopenharmony_ci hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/** 1198c2ecf20Sopenharmony_ci * igc_power_down_phy_copper - Power down copper PHY 1208c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * Power down PHY to save power when interface is down and wake on lan 1238c2ecf20Sopenharmony_ci * is not enabled. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_civoid igc_power_down_phy_copper(struct igc_hw *hw) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci u16 mii_reg = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* The PHY will retain its settings across a power down/up cycle */ 1308c2ecf20Sopenharmony_ci hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); 1318c2ecf20Sopenharmony_ci mii_reg |= MII_CR_POWER_DOWN; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Temporary workaround - should be removed when PHY will implement 1348c2ecf20Sopenharmony_ci * IEEE registers as properly 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci /* hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);*/ 1378c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/** 1418c2ecf20Sopenharmony_ci * igc_check_downshift - Checks whether a downshift in speed occurred 1428c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * Success returns 0, Failure returns 1 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * A downshift is detected by querying the PHY link health. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cis32 igc_check_downshift(struct igc_hw *hw) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 1518c2ecf20Sopenharmony_ci s32 ret_val; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci switch (phy->type) { 1548c2ecf20Sopenharmony_ci case igc_phy_i225: 1558c2ecf20Sopenharmony_ci default: 1568c2ecf20Sopenharmony_ci /* speed downshift not supported */ 1578c2ecf20Sopenharmony_ci phy->speed_downgraded = false; 1588c2ecf20Sopenharmony_ci ret_val = 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ret_val; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/** 1658c2ecf20Sopenharmony_ci * igc_phy_hw_reset - PHY hardware reset 1668c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Verify the reset block is not blocking us from resetting. Acquire 1698c2ecf20Sopenharmony_ci * semaphore (if necessary) and read/set/write the device control reset 1708c2ecf20Sopenharmony_ci * bit in the PHY. Wait the appropriate delay time for the device to 1718c2ecf20Sopenharmony_ci * reset and release the semaphore (if necessary). 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cis32 igc_phy_hw_reset(struct igc_hw *hw) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 1768c2ecf20Sopenharmony_ci u32 phpm = 0, timeout = 10000; 1778c2ecf20Sopenharmony_ci s32 ret_val; 1788c2ecf20Sopenharmony_ci u32 ctrl; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret_val = igc_check_reset_block(hw); 1818c2ecf20Sopenharmony_ci if (ret_val) { 1828c2ecf20Sopenharmony_ci ret_val = 0; 1838c2ecf20Sopenharmony_ci goto out; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret_val = phy->ops.acquire(hw); 1878c2ecf20Sopenharmony_ci if (ret_val) 1888c2ecf20Sopenharmony_ci goto out; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci phpm = rd32(IGC_I225_PHPM); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ctrl = rd32(IGC_CTRL); 1938c2ecf20Sopenharmony_ci wr32(IGC_CTRL, ctrl | IGC_CTRL_PHY_RST); 1948c2ecf20Sopenharmony_ci wrfl(); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci udelay(phy->reset_delay_us); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci wr32(IGC_CTRL, ctrl); 1998c2ecf20Sopenharmony_ci wrfl(); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* SW should guarantee 100us for the completion of the PHY reset */ 2028c2ecf20Sopenharmony_ci usleep_range(100, 150); 2038c2ecf20Sopenharmony_ci do { 2048c2ecf20Sopenharmony_ci phpm = rd32(IGC_I225_PHPM); 2058c2ecf20Sopenharmony_ci timeout--; 2068c2ecf20Sopenharmony_ci udelay(1); 2078c2ecf20Sopenharmony_ci } while (!(phpm & IGC_PHY_RST_COMP) && timeout); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!timeout) 2108c2ecf20Sopenharmony_ci hw_dbg("Timeout is expired after a phy reset\n"); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci usleep_range(100, 150); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci phy->ops.release(hw); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciout: 2178c2ecf20Sopenharmony_ci return ret_val; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * igc_phy_setup_autoneg - Configure PHY for auto-negotiation 2228c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * Reads the MII auto-neg advertisement register and/or the 1000T control 2258c2ecf20Sopenharmony_ci * register and if the PHY is already setup for auto-negotiation, then 2268c2ecf20Sopenharmony_ci * return successful. Otherwise, setup advertisement and flow control to 2278c2ecf20Sopenharmony_ci * the appropriate values for the wanted auto-negotiation. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic s32 igc_phy_setup_autoneg(struct igc_hw *hw) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 2328c2ecf20Sopenharmony_ci u16 aneg_multigbt_an_ctrl = 0; 2338c2ecf20Sopenharmony_ci u16 mii_1000t_ctrl_reg = 0; 2348c2ecf20Sopenharmony_ci u16 mii_autoneg_adv_reg; 2358c2ecf20Sopenharmony_ci s32 ret_val; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci phy->autoneg_advertised &= phy->autoneg_mask; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Read the MII Auto-Neg Advertisement Register (Address 4). */ 2408c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); 2418c2ecf20Sopenharmony_ci if (ret_val) 2428c2ecf20Sopenharmony_ci return ret_val; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_1000_FULL) { 2458c2ecf20Sopenharmony_ci /* Read the MII 1000Base-T Control Register (Address 9). */ 2468c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, 2478c2ecf20Sopenharmony_ci &mii_1000t_ctrl_reg); 2488c2ecf20Sopenharmony_ci if (ret_val) 2498c2ecf20Sopenharmony_ci return ret_val; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_2500_FULL) { 2538c2ecf20Sopenharmony_ci /* Read the MULTI GBT AN Control Register - reg 7.32 */ 2548c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK << 2558c2ecf20Sopenharmony_ci MMD_DEVADDR_SHIFT) | 2568c2ecf20Sopenharmony_ci ANEG_MULTIGBT_AN_CTRL, 2578c2ecf20Sopenharmony_ci &aneg_multigbt_an_ctrl); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (ret_val) 2608c2ecf20Sopenharmony_ci return ret_val; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Need to parse both autoneg_advertised and fc and set up 2648c2ecf20Sopenharmony_ci * the appropriate PHY registers. First we will parse for 2658c2ecf20Sopenharmony_ci * autoneg_advertised software override. Since we can advertise 2668c2ecf20Sopenharmony_ci * a plethora of combinations, we need to check each bit 2678c2ecf20Sopenharmony_ci * individually. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* First we clear all the 10/100 mb speed bits in the Auto-Neg 2718c2ecf20Sopenharmony_ci * Advertisement Register (Address 4) and the 1000 mb speed bits in 2728c2ecf20Sopenharmony_ci * the 1000Base-T Control Register (Address 9). 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | 2758c2ecf20Sopenharmony_ci NWAY_AR_100TX_HD_CAPS | 2768c2ecf20Sopenharmony_ci NWAY_AR_10T_FD_CAPS | 2778c2ecf20Sopenharmony_ci NWAY_AR_10T_HD_CAPS); 2788c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci hw_dbg("autoneg_advertised %x\n", phy->autoneg_advertised); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Do we want to advertise 10 Mb Half Duplex? */ 2838c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_10_HALF) { 2848c2ecf20Sopenharmony_ci hw_dbg("Advertise 10mb Half duplex\n"); 2858c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Do we want to advertise 10 Mb Full Duplex? */ 2898c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_10_FULL) { 2908c2ecf20Sopenharmony_ci hw_dbg("Advertise 10mb Full duplex\n"); 2918c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Do we want to advertise 100 Mb Half Duplex? */ 2958c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_100_HALF) { 2968c2ecf20Sopenharmony_ci hw_dbg("Advertise 100mb Half duplex\n"); 2978c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Do we want to advertise 100 Mb Full Duplex? */ 3018c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_100_FULL) { 3028c2ecf20Sopenharmony_ci hw_dbg("Advertise 100mb Full duplex\n"); 3038c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ 3078c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_1000_HALF) 3088c2ecf20Sopenharmony_ci hw_dbg("Advertise 1000mb Half duplex request denied!\n"); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Do we want to advertise 1000 Mb Full Duplex? */ 3118c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { 3128c2ecf20Sopenharmony_ci hw_dbg("Advertise 1000mb Full duplex\n"); 3138c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* We do not allow the Phy to advertise 2500 Mb Half Duplex */ 3178c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_2500_HALF) 3188c2ecf20Sopenharmony_ci hw_dbg("Advertise 2500mb Half duplex request denied!\n"); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Do we want to advertise 2500 Mb Full Duplex? */ 3218c2ecf20Sopenharmony_ci if (phy->autoneg_advertised & ADVERTISE_2500_FULL) { 3228c2ecf20Sopenharmony_ci hw_dbg("Advertise 2500mb Full duplex\n"); 3238c2ecf20Sopenharmony_ci aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS; 3248c2ecf20Sopenharmony_ci } else { 3258c2ecf20Sopenharmony_ci aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* Check for a software override of the flow control settings, and 3298c2ecf20Sopenharmony_ci * setup the PHY advertisement registers accordingly. If 3308c2ecf20Sopenharmony_ci * auto-negotiation is enabled, then software will have to set the 3318c2ecf20Sopenharmony_ci * "PAUSE" bits to the correct value in the Auto-Negotiation 3328c2ecf20Sopenharmony_ci * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- 3338c2ecf20Sopenharmony_ci * negotiation. 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * The possible values of the "fc" parameter are: 3368c2ecf20Sopenharmony_ci * 0: Flow control is completely disabled 3378c2ecf20Sopenharmony_ci * 1: Rx flow control is enabled (we can receive pause frames 3388c2ecf20Sopenharmony_ci * but not send pause frames). 3398c2ecf20Sopenharmony_ci * 2: Tx flow control is enabled (we can send pause frames 3408c2ecf20Sopenharmony_ci * but we do not support receiving pause frames). 3418c2ecf20Sopenharmony_ci * 3: Both Rx and Tx flow control (symmetric) are enabled. 3428c2ecf20Sopenharmony_ci * other: No software override. The flow control configuration 3438c2ecf20Sopenharmony_ci * in the EEPROM is used. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci switch (hw->fc.current_mode) { 3468c2ecf20Sopenharmony_ci case igc_fc_none: 3478c2ecf20Sopenharmony_ci /* Flow control (Rx & Tx) is completely disabled by a 3488c2ecf20Sopenharmony_ci * software over-ride. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case igc_fc_rx_pause: 3538c2ecf20Sopenharmony_ci /* Rx Flow control is enabled, and Tx Flow control is 3548c2ecf20Sopenharmony_ci * disabled, by a software over-ride. 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * Since there really isn't a way to advertise that we are 3578c2ecf20Sopenharmony_ci * capable of Rx Pause ONLY, we will advertise that we 3588c2ecf20Sopenharmony_ci * support both symmetric and asymmetric Rx PAUSE. Later 3598c2ecf20Sopenharmony_ci * (in igc_config_fc_after_link_up) we will disable the 3608c2ecf20Sopenharmony_ci * hw's ability to send PAUSE frames. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case igc_fc_tx_pause: 3658c2ecf20Sopenharmony_ci /* Tx Flow control is enabled, and Rx Flow control is 3668c2ecf20Sopenharmony_ci * disabled, by a software over-ride. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; 3698c2ecf20Sopenharmony_ci mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case igc_fc_full: 3728c2ecf20Sopenharmony_ci /* Flow control (both Rx and Tx) is enabled by a software 3738c2ecf20Sopenharmony_ci * over-ride. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci default: 3788c2ecf20Sopenharmony_ci hw_dbg("Flow control param set incorrectly\n"); 3798c2ecf20Sopenharmony_ci return -IGC_ERR_CONFIG; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); 3838c2ecf20Sopenharmony_ci if (ret_val) 3848c2ecf20Sopenharmony_ci return ret_val; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci hw_dbg("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_1000_FULL) 3898c2ecf20Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, 3908c2ecf20Sopenharmony_ci mii_1000t_ctrl_reg); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (phy->autoneg_mask & ADVERTISE_2500_FULL) 3938c2ecf20Sopenharmony_ci ret_val = phy->ops.write_reg(hw, 3948c2ecf20Sopenharmony_ci (STANDARD_AN_REG_MASK << 3958c2ecf20Sopenharmony_ci MMD_DEVADDR_SHIFT) | 3968c2ecf20Sopenharmony_ci ANEG_MULTIGBT_AN_CTRL, 3978c2ecf20Sopenharmony_ci aneg_multigbt_an_ctrl); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return ret_val; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/** 4038c2ecf20Sopenharmony_ci * igc_wait_autoneg - Wait for auto-neg completion 4048c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * Waits for auto-negotiation to complete or for the auto-negotiation time 4078c2ecf20Sopenharmony_ci * limit to expire, which ever happens first. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_cistatic s32 igc_wait_autoneg(struct igc_hw *hw) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci u16 i, phy_status; 4128c2ecf20Sopenharmony_ci s32 ret_val = 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ 4158c2ecf20Sopenharmony_ci for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { 4168c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 4178c2ecf20Sopenharmony_ci if (ret_val) 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); 4208c2ecf20Sopenharmony_ci if (ret_val) 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci if (phy_status & MII_SR_AUTONEG_COMPLETE) 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci msleep(100); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation 4288c2ecf20Sopenharmony_ci * has completed. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci return ret_val; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/** 4348c2ecf20Sopenharmony_ci * igc_copper_link_autoneg - Setup/Enable autoneg for copper link 4358c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * Performs initial bounds checking on autoneg advertisement parameter, then 4388c2ecf20Sopenharmony_ci * configure to advertise the full capability. Setup the PHY to autoneg 4398c2ecf20Sopenharmony_ci * and restart the negotiation process between the link partner. If 4408c2ecf20Sopenharmony_ci * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic s32 igc_copper_link_autoneg(struct igc_hw *hw) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 4458c2ecf20Sopenharmony_ci u16 phy_ctrl; 4468c2ecf20Sopenharmony_ci s32 ret_val; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Perform some bounds checking on the autoneg advertisement 4498c2ecf20Sopenharmony_ci * parameter. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci phy->autoneg_advertised &= phy->autoneg_mask; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* If autoneg_advertised is zero, we assume it was not defaulted 4548c2ecf20Sopenharmony_ci * by the calling code so we set to advertise full capability. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci if (phy->autoneg_advertised == 0) 4578c2ecf20Sopenharmony_ci phy->autoneg_advertised = phy->autoneg_mask; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci hw_dbg("Reconfiguring auto-neg advertisement params\n"); 4608c2ecf20Sopenharmony_ci ret_val = igc_phy_setup_autoneg(hw); 4618c2ecf20Sopenharmony_ci if (ret_val) { 4628c2ecf20Sopenharmony_ci hw_dbg("Error Setting up Auto-Negotiation\n"); 4638c2ecf20Sopenharmony_ci goto out; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci hw_dbg("Restarting Auto-Neg\n"); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Restart auto-negotiation by setting the Auto Neg Enable bit and 4688c2ecf20Sopenharmony_ci * the Auto Neg Restart bit in the PHY control register. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); 4718c2ecf20Sopenharmony_ci if (ret_val) 4728c2ecf20Sopenharmony_ci goto out; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); 4758c2ecf20Sopenharmony_ci ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); 4768c2ecf20Sopenharmony_ci if (ret_val) 4778c2ecf20Sopenharmony_ci goto out; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Does the user want to wait for Auto-Neg to complete here, or 4808c2ecf20Sopenharmony_ci * check at a later time (for example, callback routine). 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci if (phy->autoneg_wait_to_complete) { 4838c2ecf20Sopenharmony_ci ret_val = igc_wait_autoneg(hw); 4848c2ecf20Sopenharmony_ci if (ret_val) { 4858c2ecf20Sopenharmony_ci hw_dbg("Error while waiting for autoneg to complete\n"); 4868c2ecf20Sopenharmony_ci goto out; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci hw->mac.get_link_status = true; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciout: 4938c2ecf20Sopenharmony_ci return ret_val; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/** 4978c2ecf20Sopenharmony_ci * igc_setup_copper_link - Configure copper link settings 4988c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * Calls the appropriate function to configure the link for auto-neg or forced 5018c2ecf20Sopenharmony_ci * speed and duplex. Then we check for link, once link is established calls 5028c2ecf20Sopenharmony_ci * to configure collision distance and flow control are called. If link is 5038c2ecf20Sopenharmony_ci * not established, we return -IGC_ERR_PHY (-2). 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cis32 igc_setup_copper_link(struct igc_hw *hw) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci s32 ret_val = 0; 5088c2ecf20Sopenharmony_ci bool link; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (hw->mac.autoneg) { 5118c2ecf20Sopenharmony_ci /* Setup autoneg and flow control advertisement and perform 5128c2ecf20Sopenharmony_ci * autonegotiation. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci ret_val = igc_copper_link_autoneg(hw); 5158c2ecf20Sopenharmony_ci if (ret_val) 5168c2ecf20Sopenharmony_ci goto out; 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci /* PHY will be set to 10H, 10F, 100H or 100F 5198c2ecf20Sopenharmony_ci * depending on user settings. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci hw_dbg("Forcing Speed and Duplex\n"); 5228c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.force_speed_duplex(hw); 5238c2ecf20Sopenharmony_ci if (ret_val) { 5248c2ecf20Sopenharmony_ci hw_dbg("Error Forcing Speed and Duplex\n"); 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* Check link status. Wait up to 100 microseconds for link to become 5308c2ecf20Sopenharmony_ci * valid. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci ret_val = igc_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link); 5338c2ecf20Sopenharmony_ci if (ret_val) 5348c2ecf20Sopenharmony_ci goto out; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (link) { 5378c2ecf20Sopenharmony_ci hw_dbg("Valid link established!!!\n"); 5388c2ecf20Sopenharmony_ci igc_config_collision_dist(hw); 5398c2ecf20Sopenharmony_ci ret_val = igc_config_fc_after_link_up(hw); 5408c2ecf20Sopenharmony_ci } else { 5418c2ecf20Sopenharmony_ci hw_dbg("Unable to establish link!!!\n"); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciout: 5458c2ecf20Sopenharmony_ci return ret_val; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/** 5498c2ecf20Sopenharmony_ci * igc_read_phy_reg_mdic - Read MDI control register 5508c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 5518c2ecf20Sopenharmony_ci * @offset: register offset to be read 5528c2ecf20Sopenharmony_ci * @data: pointer to the read data 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * Reads the MDI control register in the PHY at offset and stores the 5558c2ecf20Sopenharmony_ci * information read to data. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic s32 igc_read_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 *data) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 5608c2ecf20Sopenharmony_ci u32 i, mdic = 0; 5618c2ecf20Sopenharmony_ci s32 ret_val = 0; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (offset > MAX_PHY_REG_ADDRESS) { 5648c2ecf20Sopenharmony_ci hw_dbg("PHY Address %d is out of range\n", offset); 5658c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PARAM; 5668c2ecf20Sopenharmony_ci goto out; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Set up Op-code, Phy Address, and register offset in the MDI 5708c2ecf20Sopenharmony_ci * Control register. The MAC will take care of interfacing with the 5718c2ecf20Sopenharmony_ci * PHY to retrieve the desired data. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci mdic = ((offset << IGC_MDIC_REG_SHIFT) | 5748c2ecf20Sopenharmony_ci (phy->addr << IGC_MDIC_PHY_SHIFT) | 5758c2ecf20Sopenharmony_ci (IGC_MDIC_OP_READ)); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci wr32(IGC_MDIC, mdic); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Poll the ready bit to see if the MDI read completed 5808c2ecf20Sopenharmony_ci * Increasing the time out as testing showed failures with 5818c2ecf20Sopenharmony_ci * the lower time out 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci for (i = 0; i < IGC_GEN_POLL_TIMEOUT; i++) { 5848c2ecf20Sopenharmony_ci udelay(50); 5858c2ecf20Sopenharmony_ci mdic = rd32(IGC_MDIC); 5868c2ecf20Sopenharmony_ci if (mdic & IGC_MDIC_READY) 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci if (!(mdic & IGC_MDIC_READY)) { 5908c2ecf20Sopenharmony_ci hw_dbg("MDI Read did not complete\n"); 5918c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PHY; 5928c2ecf20Sopenharmony_ci goto out; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci if (mdic & IGC_MDIC_ERROR) { 5958c2ecf20Sopenharmony_ci hw_dbg("MDI Error\n"); 5968c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PHY; 5978c2ecf20Sopenharmony_ci goto out; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci *data = (u16)mdic; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciout: 6028c2ecf20Sopenharmony_ci return ret_val; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci/** 6068c2ecf20Sopenharmony_ci * igc_write_phy_reg_mdic - Write MDI control register 6078c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 6088c2ecf20Sopenharmony_ci * @offset: register offset to write to 6098c2ecf20Sopenharmony_ci * @data: data to write to register at offset 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * Writes data to MDI control register in the PHY at offset. 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_cistatic s32 igc_write_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 data) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct igc_phy_info *phy = &hw->phy; 6168c2ecf20Sopenharmony_ci u32 i, mdic = 0; 6178c2ecf20Sopenharmony_ci s32 ret_val = 0; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (offset > MAX_PHY_REG_ADDRESS) { 6208c2ecf20Sopenharmony_ci hw_dbg("PHY Address %d is out of range\n", offset); 6218c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PARAM; 6228c2ecf20Sopenharmony_ci goto out; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Set up Op-code, Phy Address, and register offset in the MDI 6268c2ecf20Sopenharmony_ci * Control register. The MAC will take care of interfacing with the 6278c2ecf20Sopenharmony_ci * PHY to write the desired data. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci mdic = (((u32)data) | 6308c2ecf20Sopenharmony_ci (offset << IGC_MDIC_REG_SHIFT) | 6318c2ecf20Sopenharmony_ci (phy->addr << IGC_MDIC_PHY_SHIFT) | 6328c2ecf20Sopenharmony_ci (IGC_MDIC_OP_WRITE)); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci wr32(IGC_MDIC, mdic); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Poll the ready bit to see if the MDI read completed 6378c2ecf20Sopenharmony_ci * Increasing the time out as testing showed failures with 6388c2ecf20Sopenharmony_ci * the lower time out 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci for (i = 0; i < IGC_GEN_POLL_TIMEOUT; i++) { 6418c2ecf20Sopenharmony_ci udelay(50); 6428c2ecf20Sopenharmony_ci mdic = rd32(IGC_MDIC); 6438c2ecf20Sopenharmony_ci if (mdic & IGC_MDIC_READY) 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci if (!(mdic & IGC_MDIC_READY)) { 6478c2ecf20Sopenharmony_ci hw_dbg("MDI Write did not complete\n"); 6488c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PHY; 6498c2ecf20Sopenharmony_ci goto out; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci if (mdic & IGC_MDIC_ERROR) { 6528c2ecf20Sopenharmony_ci hw_dbg("MDI Error\n"); 6538c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_PHY; 6548c2ecf20Sopenharmony_ci goto out; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ciout: 6588c2ecf20Sopenharmony_ci return ret_val; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/** 6628c2ecf20Sopenharmony_ci * __igc_access_xmdio_reg - Read/write XMDIO register 6638c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 6648c2ecf20Sopenharmony_ci * @address: XMDIO address to program 6658c2ecf20Sopenharmony_ci * @dev_addr: device address to program 6668c2ecf20Sopenharmony_ci * @data: pointer to value to read/write from/to the XMDIO address 6678c2ecf20Sopenharmony_ci * @read: boolean flag to indicate read or write 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_cistatic s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address, 6708c2ecf20Sopenharmony_ci u8 dev_addr, u16 *data, bool read) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci s32 ret_val; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr); 6758c2ecf20Sopenharmony_ci if (ret_val) 6768c2ecf20Sopenharmony_ci return ret_val; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address); 6798c2ecf20Sopenharmony_ci if (ret_val) 6808c2ecf20Sopenharmony_ci return ret_val; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA | 6838c2ecf20Sopenharmony_ci dev_addr); 6848c2ecf20Sopenharmony_ci if (ret_val) 6858c2ecf20Sopenharmony_ci return ret_val; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (read) 6888c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data); 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data); 6918c2ecf20Sopenharmony_ci if (ret_val) 6928c2ecf20Sopenharmony_ci return ret_val; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* Recalibrate the device back to 0 */ 6958c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0); 6968c2ecf20Sopenharmony_ci if (ret_val) 6978c2ecf20Sopenharmony_ci return ret_val; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return ret_val; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * igc_read_xmdio_reg - Read XMDIO register 7048c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 7058c2ecf20Sopenharmony_ci * @addr: XMDIO address to program 7068c2ecf20Sopenharmony_ci * @dev_addr: device address to program 7078c2ecf20Sopenharmony_ci * @data: value to be read from the EMI address 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_cistatic s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, 7108c2ecf20Sopenharmony_ci u8 dev_addr, u16 *data) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** 7168c2ecf20Sopenharmony_ci * igc_write_xmdio_reg - Write XMDIO register 7178c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 7188c2ecf20Sopenharmony_ci * @addr: XMDIO address to program 7198c2ecf20Sopenharmony_ci * @dev_addr: device address to program 7208c2ecf20Sopenharmony_ci * @data: value to be written to the XMDIO address 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_cistatic s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, 7238c2ecf20Sopenharmony_ci u8 dev_addr, u16 data) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/** 7298c2ecf20Sopenharmony_ci * igc_write_phy_reg_gpy - Write GPY PHY register 7308c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 7318c2ecf20Sopenharmony_ci * @offset: register offset to write to 7328c2ecf20Sopenharmony_ci * @data: data to write at register offset 7338c2ecf20Sopenharmony_ci * 7348c2ecf20Sopenharmony_ci * Acquires semaphore, if necessary, then writes the data to PHY register 7358c2ecf20Sopenharmony_ci * at the offset. Release any acquired semaphores before exiting. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_cis32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 7408c2ecf20Sopenharmony_ci s32 ret_val; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci offset = offset & GPY_REG_MASK; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (!dev_addr) { 7458c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.acquire(hw); 7468c2ecf20Sopenharmony_ci if (ret_val) 7478c2ecf20Sopenharmony_ci return ret_val; 7488c2ecf20Sopenharmony_ci ret_val = igc_write_phy_reg_mdic(hw, offset, data); 7498c2ecf20Sopenharmony_ci hw->phy.ops.release(hw); 7508c2ecf20Sopenharmony_ci } else { 7518c2ecf20Sopenharmony_ci ret_val = igc_write_xmdio_reg(hw, (u16)offset, dev_addr, 7528c2ecf20Sopenharmony_ci data); 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return ret_val; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/** 7598c2ecf20Sopenharmony_ci * igc_read_phy_reg_gpy - Read GPY PHY register 7608c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 7618c2ecf20Sopenharmony_ci * @offset: lower half is register offset to read to 7628c2ecf20Sopenharmony_ci * upper half is MMD to use. 7638c2ecf20Sopenharmony_ci * @data: data to read at register offset 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * Acquires semaphore, if necessary, then reads the data in the PHY register 7668c2ecf20Sopenharmony_ci * at the offset. Release any acquired semaphores before exiting. 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_cis32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; 7718c2ecf20Sopenharmony_ci s32 ret_val; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci offset = offset & GPY_REG_MASK; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!dev_addr) { 7768c2ecf20Sopenharmony_ci ret_val = hw->phy.ops.acquire(hw); 7778c2ecf20Sopenharmony_ci if (ret_val) 7788c2ecf20Sopenharmony_ci return ret_val; 7798c2ecf20Sopenharmony_ci ret_val = igc_read_phy_reg_mdic(hw, offset, data); 7808c2ecf20Sopenharmony_ci hw->phy.ops.release(hw); 7818c2ecf20Sopenharmony_ci } else { 7828c2ecf20Sopenharmony_ci ret_val = igc_read_xmdio_reg(hw, (u16)offset, dev_addr, 7838c2ecf20Sopenharmony_ci data); 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return ret_val; 7878c2ecf20Sopenharmony_ci} 788