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