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