Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4eb80801 authored by Sasha Neftin's avatar Sasha Neftin Committed by Jeff Kirsher
Browse files

igc: Add setup link functionality



Add link establishment methods
Add auto negotiation methods
Add read MAC address method

Signed-off-by: default avatarSasha Neftin <sasha.neftin@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 5586838f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -314,6 +314,7 @@ struct igc_adapter {
	struct work_struct reset_task;
	struct work_struct reset_task;
	struct work_struct watchdog_task;
	struct work_struct watchdog_task;
	struct work_struct dma_err_task;
	struct work_struct dma_err_task;
	bool fc_autoneg;


	u8 tx_timeout_factor;
	u8 tx_timeout_factor;


+40 −0
Original line number Original line Diff line number Diff line
@@ -177,6 +177,29 @@ static s32 igc_init_nvm_params_base(struct igc_hw *hw)
	return 0;
	return 0;
}
}


/**
 * igc_setup_copper_link_base - Configure copper link settings
 * @hw: pointer to the HW structure
 *
 * Configures the link for auto-neg or forced speed and duplex.  Then we check
 * for link, once link is established calls to configure collision distance
 * and flow control are called.
 */
static s32 igc_setup_copper_link_base(struct igc_hw *hw)
{
	s32  ret_val = 0;
	u32 ctrl;

	ctrl = rd32(IGC_CTRL);
	ctrl |= IGC_CTRL_SLU;
	ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
	wr32(IGC_CTRL, ctrl);

	ret_val = igc_setup_copper_link(hw);

	return ret_val;
}

/**
/**
 * igc_init_mac_params_base - Init MAC func ptrs.
 * igc_init_mac_params_base - Init MAC func ptrs.
 * @hw: pointer to the HW structure
 * @hw: pointer to the HW structure
@@ -200,6 +223,9 @@ static s32 igc_init_mac_params_base(struct igc_hw *hw)
	if (mac->type == igc_i225)
	if (mac->type == igc_i225)
		dev_spec->clear_semaphore_once = true;
		dev_spec->clear_semaphore_once = true;


	/* physical interface link setup */
	mac->ops.setup_physical_interface = igc_setup_copper_link_base;

	return 0;
	return 0;
}
}


@@ -242,6 +268,8 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)
	if (ret_val)
	if (ret_val)
		return ret_val;
		return ret_val;


	igc_check_for_link_base(hw);

	/* Verify phy id and set remaining function pointers */
	/* Verify phy id and set remaining function pointers */
	switch (phy->id) {
	switch (phy->id) {
	case I225_I_PHY_ID:
	case I225_I_PHY_ID:
@@ -258,10 +286,22 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)


static s32 igc_get_invariants_base(struct igc_hw *hw)
static s32 igc_get_invariants_base(struct igc_hw *hw)
{
{
	struct igc_mac_info *mac = &hw->mac;
	u32 link_mode = 0;
	u32 link_mode = 0;
	u32 ctrl_ext = 0;
	u32 ctrl_ext = 0;
	s32 ret_val = 0;
	s32 ret_val = 0;


	switch (hw->device_id) {
	case IGC_DEV_ID_I225_LM:
	case IGC_DEV_ID_I225_V:
		mac->type = igc_i225;
		break;
	default:
		return -IGC_ERR_MAC_INIT;
	}

	hw->phy.media_type = igc_media_type_copper;

	ctrl_ext = rd32(IGC_CTRL_EXT);
	ctrl_ext = rd32(IGC_CTRL_EXT);
	link_mode = ctrl_ext & IGC_CTRL_EXT_LINK_MODE_MASK;
	link_mode = ctrl_ext & IGC_CTRL_EXT_LINK_MODE_MASK;


+38 −0
Original line number Original line Diff line number Diff line
@@ -13,6 +13,11 @@
/* Physical Func Reset Done Indication */
/* Physical Func Reset Done Indication */
#define IGC_CTRL_EXT_LINK_MODE_MASK	0x00C00000
#define IGC_CTRL_EXT_LINK_MODE_MASK	0x00C00000


/* Loop limit on how long we wait for auto-negotiation to complete */
#define COPPER_LINK_UP_LIMIT		10
#define PHY_AUTO_NEG_LIMIT		45
#define PHY_FORCE_LIMIT			20

/* Number of 100 microseconds we wait for PCI Express master disable */
/* Number of 100 microseconds we wait for PCI Express master disable */
#define MASTER_DISABLE_TIMEOUT		800
#define MASTER_DISABLE_TIMEOUT		800
/*Blocks new Master requests */
/*Blocks new Master requests */
@@ -54,6 +59,12 @@
#define IGC_CTRL_RST		0x04000000  /* Global reset */
#define IGC_CTRL_RST		0x04000000  /* Global reset */


#define IGC_CTRL_PHY_RST	0x80000000  /* PHY Reset */
#define IGC_CTRL_PHY_RST	0x80000000  /* PHY Reset */
#define IGC_CTRL_SLU		0x00000040  /* Set link up (Force Link) */
#define IGC_CTRL_FRCSPD		0x00000800  /* Force Speed */
#define IGC_CTRL_FRCDPX		0x00001000  /* Force Duplex */

#define IGC_CTRL_RFCE		0x08000000  /* Receive Flow Control enable */
#define IGC_CTRL_TFCE		0x10000000  /* Transmit flow control enable */


/* PBA constants */
/* PBA constants */
#define IGC_PBA_34K		0x0022
#define IGC_PBA_34K		0x0022
@@ -66,6 +77,29 @@
#define IGC_SWFW_EEP_SM		0x1
#define IGC_SWFW_EEP_SM		0x1
#define IGC_SWFW_PHY0_SM	0x2
#define IGC_SWFW_PHY0_SM	0x2


/* Autoneg Advertisement Register */
#define NWAY_AR_10T_HD_CAPS	0x0020   /* 10T   Half Duplex Capable */
#define NWAY_AR_10T_FD_CAPS	0x0040   /* 10T   Full Duplex Capable */
#define NWAY_AR_100TX_HD_CAPS	0x0080   /* 100TX Half Duplex Capable */
#define NWAY_AR_100TX_FD_CAPS	0x0100   /* 100TX Full Duplex Capable */
#define NWAY_AR_PAUSE		0x0400   /* Pause operation desired */
#define NWAY_AR_ASM_DIR		0x0800   /* Asymmetric Pause Direction bit */

/* Link Partner Ability Register (Base Page) */
#define NWAY_LPAR_PAUSE		0x0400 /* LP Pause operation desired */
#define NWAY_LPAR_ASM_DIR	0x0800 /* LP Asymmetric Pause Direction bit */

/* 1000BASE-T Control Register */
#define CR_1000T_ASYM_PAUSE	0x0080 /* Advertise asymmetric pause bit */
#define CR_1000T_HD_CAPS	0x0100 /* Advertise 1000T HD capability */
#define CR_1000T_FD_CAPS	0x0200 /* Advertise 1000T FD capability  */

/* PHY GPY 211 registers */
#define STANDARD_AN_REG_MASK	0x0007 /* MMD */
#define ANEG_MULTIGBT_AN_CTRL	0x0020 /* MULTI GBT AN Control Register */
#define MMD_DEVADDR_SHIFT	16     /* Shift MMD to higher bits */
#define CR_2500T_FD_CAPS	0x0080 /* Advertise 2500T FD capability */

/* NVM Control */
/* NVM Control */
/* Number of milliseconds for NVM auto read done after MAC reset. */
/* Number of milliseconds for NVM auto read done after MAC reset. */
#define AUTO_READ_DONE_TIMEOUT		10
#define AUTO_READ_DONE_TIMEOUT		10
@@ -318,6 +352,10 @@
#define PHY_STATUS		0x01 /* Status Register */
#define PHY_STATUS		0x01 /* Status Register */
#define PHY_ID1			0x02 /* Phy Id Reg (word 1) */
#define PHY_ID1			0x02 /* Phy Id Reg (word 1) */
#define PHY_ID2			0x03 /* Phy Id Reg (word 2) */
#define PHY_ID2			0x03 /* Phy Id Reg (word 2) */
#define PHY_AUTONEG_ADV		0x04 /* Autoneg Advertisement */
#define PHY_LP_ABILITY		0x05 /* Link Partner Ability (Base Page) */
#define PHY_1000T_CTRL		0x09 /* 1000Base-T Control Reg */
#define PHY_1000T_STATUS	0x0A /* 1000Base-T Status Reg */


/* Bit definitions for valid PHY IDs. I = Integrated E = External */
/* Bit definitions for valid PHY IDs. I = Integrated E = External */
#define I225_I_PHY_ID		0x67C9DC00
#define I225_I_PHY_ID		0x67C9DC00
+271 −0
Original line number Original line Diff line number Diff line
@@ -92,6 +92,8 @@ s32 igc_setup_link(struct igc_hw *hw)
	/* In the case of the phy reset being blocked, we already have a link.
	/* In the case of the phy reset being blocked, we already have a link.
	 * We do not need to set it up again.
	 * We do not need to set it up again.
	 */
	 */
	if (igc_check_reset_block(hw))
		goto out;


	/* If requested flow control is set to default, set flow control
	/* If requested flow control is set to default, set flow control
	 * based on the EEPROM flow control settings.
	 * based on the EEPROM flow control settings.
@@ -142,9 +144,73 @@ s32 igc_setup_link(struct igc_hw *hw)
 */
 */
static s32 igc_set_default_fc(struct igc_hw *hw)
static s32 igc_set_default_fc(struct igc_hw *hw)
{
{
	hw->fc.requested_mode = igc_fc_full;
	return 0;
	return 0;
}
}


/**
 * igc_force_mac_fc - Force the MAC's flow control settings
 * @hw: pointer to the HW structure
 *
 * Force the MAC's flow control settings.  Sets the TFCE and RFCE bits in the
 * device control register to reflect the adapter settings.  TFCE and RFCE
 * need to be explicitly set by software when a copper PHY is used because
 * autonegotiation is managed by the PHY rather than the MAC.  Software must
 * also configure these bits when link is forced on a fiber connection.
 */
s32 igc_force_mac_fc(struct igc_hw *hw)
{
	s32 ret_val = 0;
	u32 ctrl;

	ctrl = rd32(IGC_CTRL);

	/* Because we didn't get link via the internal auto-negotiation
	 * mechanism (we either forced link or we got link via PHY
	 * auto-neg), we have to manually enable/disable transmit an
	 * receive flow control.
	 *
	 * The "Case" statement below enables/disable flow control
	 * according to the "hw->fc.current_mode" parameter.
	 *
	 * The possible values of the "fc" parameter are:
	 *      0:  Flow control is completely disabled
	 *      1:  Rx flow control is enabled (we can receive pause
	 *          frames but not send pause frames).
	 *      2:  Tx flow control is enabled (we can send pause frames
	 *          frames but we do not receive pause frames).
	 *      3:  Both Rx and TX flow control (symmetric) is enabled.
	 *  other:  No other values should be possible at this point.
	 */
	hw_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode);

	switch (hw->fc.current_mode) {
	case igc_fc_none:
		ctrl &= (~(IGC_CTRL_TFCE | IGC_CTRL_RFCE));
		break;
	case igc_fc_rx_pause:
		ctrl &= (~IGC_CTRL_TFCE);
		ctrl |= IGC_CTRL_RFCE;
		break;
	case igc_fc_tx_pause:
		ctrl &= (~IGC_CTRL_RFCE);
		ctrl |= IGC_CTRL_TFCE;
		break;
	case igc_fc_full:
		ctrl |= (IGC_CTRL_TFCE | IGC_CTRL_RFCE);
		break;
	default:
		hw_dbg("Flow control param set incorrectly\n");
		ret_val = -IGC_ERR_CONFIG;
		goto out;
	}

	wr32(IGC_CTRL, ctrl);

out:
	return ret_val;
}

/**
/**
 * igc_set_fc_watermarks - Set flow control high/low watermarks
 * igc_set_fc_watermarks - Set flow control high/low watermarks
 * @hw: pointer to the HW structure
 * @hw: pointer to the HW structure
@@ -371,6 +437,7 @@ s32 igc_check_for_copper_link(struct igc_hw *hw)
	 * settings because we may have had to re-autoneg with a
	 * settings because we may have had to re-autoneg with a
	 * different link partner.
	 * different link partner.
	 */
	 */
	ret_val = igc_config_fc_after_link_up(hw);
	if (ret_val)
	if (ret_val)
		hw_dbg("Error configuring flow control\n");
		hw_dbg("Error configuring flow control\n");


@@ -399,6 +466,210 @@ void igc_config_collision_dist(struct igc_hw *hw)
	wrfl();
	wrfl();
}
}


/**
 * igc_config_fc_after_link_up - Configures flow control after link
 * @hw: pointer to the HW structure
 *
 * Checks the status of auto-negotiation after link up to ensure that the
 * speed and duplex were not forced.  If the link needed to be forced, then
 * flow control needs to be forced also.  If auto-negotiation is enabled
 * and did not fail, then we configure flow control based on our link
 * partner.
 */
s32 igc_config_fc_after_link_up(struct igc_hw *hw)
{
	u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
	struct igc_mac_info *mac = &hw->mac;
	u16 speed, duplex;
	s32 ret_val = 0;

	/* Check for the case where we have fiber media and auto-neg failed
	 * so we had to force link.  In this case, we need to force the
	 * configuration of the MAC to match the "fc" parameter.
	 */
	if (mac->autoneg_failed) {
		if (hw->phy.media_type == igc_media_type_copper)
			ret_val = igc_force_mac_fc(hw);
	}

	if (ret_val) {
		hw_dbg("Error forcing flow control settings\n");
		goto out;
	}

	/* Check for the case where we have copper media and auto-neg is
	 * enabled.  In this case, we need to check and see if Auto-Neg
	 * has completed, and if so, how the PHY and link partner has
	 * flow control configured.
	 */
	if (hw->phy.media_type == igc_media_type_copper && mac->autoneg) {
		/* Read the MII Status Register and check to see if AutoNeg
		 * has completed.  We read this twice because this reg has
		 * some "sticky" (latched) bits.
		 */
		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
					       &mii_status_reg);
		if (ret_val)
			goto out;
		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
					       &mii_status_reg);
		if (ret_val)
			goto out;

		if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
			hw_dbg("Copper PHY and Auto Neg has not completed.\n");
			goto out;
		}

		/* The AutoNeg process has completed, so we now need to
		 * read both the Auto Negotiation Advertisement
		 * Register (Address 4) and the Auto_Negotiation Base
		 * Page Ability Register (Address 5) to determine how
		 * flow control was negotiated.
		 */
		ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
					       &mii_nway_adv_reg);
		if (ret_val)
			goto out;
		ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
					       &mii_nway_lp_ability_reg);
		if (ret_val)
			goto out;
		/* Two bits in the Auto Negotiation Advertisement Register
		 * (Address 4) and two bits in the Auto Negotiation Base
		 * Page Ability Register (Address 5) determine flow control
		 * for both the PHY and the link partner.  The following
		 * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
		 * 1999, describes these PAUSE resolution bits and how flow
		 * control is determined based upon these settings.
		 * NOTE:  DC = Don't Care
		 *
		 *   LOCAL DEVICE  |   LINK PARTNER
		 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
		 *-------|---------|-------|---------|--------------------
		 *   0   |    0    |  DC   |   DC    | igc_fc_none
		 *   0   |    1    |   0   |   DC    | igc_fc_none
		 *   0   |    1    |   1   |    0    | igc_fc_none
		 *   0   |    1    |   1   |    1    | igc_fc_tx_pause
		 *   1   |    0    |   0   |   DC    | igc_fc_none
		 *   1   |   DC    |   1   |   DC    | igc_fc_full
		 *   1   |    1    |   0   |    0    | igc_fc_none
		 *   1   |    1    |   0   |    1    | igc_fc_rx_pause
		 *
		 * Are both PAUSE bits set to 1?  If so, this implies
		 * Symmetric Flow Control is enabled at both ends.  The
		 * ASM_DIR bits are irrelevant per the spec.
		 *
		 * For Symmetric Flow Control:
		 *
		 *   LOCAL DEVICE  |   LINK PARTNER
		 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
		 *-------|---------|-------|---------|--------------------
		 *   1   |   DC    |   1   |   DC    | IGC_fc_full
		 *
		 */
		if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
		    (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
			/* Now we need to check if the user selected RX ONLY
			 * of pause frames.  In this case, we had to advertise
			 * FULL flow control because we could not advertise RX
			 * ONLY. Hence, we must now check to see if we need to
			 * turn OFF  the TRANSMISSION of PAUSE frames.
			 */
			if (hw->fc.requested_mode == igc_fc_full) {
				hw->fc.current_mode = igc_fc_full;
				hw_dbg("Flow Control = FULL.\n");
			} else {
				hw->fc.current_mode = igc_fc_rx_pause;
				hw_dbg("Flow Control = RX PAUSE frames only.\n");
			}
		}

		/* For receiving PAUSE frames ONLY.
		 *
		 *   LOCAL DEVICE  |   LINK PARTNER
		 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
		 *-------|---------|-------|---------|--------------------
		 *   0   |    1    |   1   |    1    | igc_fc_tx_pause
		 */
		else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
			 (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
			 (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
			 (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
			hw->fc.current_mode = igc_fc_tx_pause;
			hw_dbg("Flow Control = TX PAUSE frames only.\n");
		}
		/* For transmitting PAUSE frames ONLY.
		 *
		 *   LOCAL DEVICE  |   LINK PARTNER
		 * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
		 *-------|---------|-------|---------|--------------------
		 *   1   |    1    |   0   |    1    | igc_fc_rx_pause
		 */
		else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
			 (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
			 !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
			 (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
			hw->fc.current_mode = igc_fc_rx_pause;
			hw_dbg("Flow Control = RX PAUSE frames only.\n");
		}
		/* Per the IEEE spec, at this point flow control should be
		 * disabled.  However, we want to consider that we could
		 * be connected to a legacy switch that doesn't advertise
		 * desired flow control, but can be forced on the link
		 * partner.  So if we advertised no flow control, that is
		 * what we will resolve to.  If we advertised some kind of
		 * receive capability (Rx Pause Only or Full Flow Control)
		 * and the link partner advertised none, we will configure
		 * ourselves to enable Rx Flow Control only.  We can do
		 * this safely for two reasons:  If the link partner really
		 * didn't want flow control enabled, and we enable Rx, no
		 * harm done since we won't be receiving any PAUSE frames
		 * anyway.  If the intent on the link partner was to have
		 * flow control enabled, then by us enabling RX only, we
		 * can at least receive pause frames and process them.
		 * This is a good idea because in most cases, since we are
		 * predominantly a server NIC, more times than not we will
		 * be asked to delay transmission of packets than asking
		 * our link partner to pause transmission of frames.
		 */
		else if ((hw->fc.requested_mode == igc_fc_none) ||
			 (hw->fc.requested_mode == igc_fc_tx_pause) ||
			 (hw->fc.strict_ieee)) {
			hw->fc.current_mode = igc_fc_none;
			hw_dbg("Flow Control = NONE.\n");
		} else {
			hw->fc.current_mode = igc_fc_rx_pause;
			hw_dbg("Flow Control = RX PAUSE frames only.\n");
		}

		/* Now we need to do one last check...  If we auto-
		 * negotiated to HALF DUPLEX, flow control should not be
		 * enabled per IEEE 802.3 spec.
		 */
		ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
		if (ret_val) {
			hw_dbg("Error getting link speed and duplex\n");
			goto out;
		}

		if (duplex == HALF_DUPLEX)
			hw->fc.current_mode = igc_fc_none;

		/* Now we call a subroutine to actually force the MAC
		 * controller to use the correct flow control settings.
		 */
		ret_val = igc_force_mac_fc(hw);
		if (ret_val) {
			hw_dbg("Error forcing flow control settings\n");
			goto out;
		}
	}

out:
	return 0;
}

/**
/**
 * igc_get_auto_rd_done - Check for auto read completion
 * igc_get_auto_rd_done - Check for auto read completion
 * @hw: pointer to the HW structure
 * @hw: pointer to the HW structure
+2 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,8 @@
/* forward declaration */
/* forward declaration */
s32 igc_disable_pcie_master(struct igc_hw *hw);
s32 igc_disable_pcie_master(struct igc_hw *hw);
s32 igc_check_for_copper_link(struct igc_hw *hw);
s32 igc_check_for_copper_link(struct igc_hw *hw);
s32 igc_config_fc_after_link_up(struct igc_hw *hw);
s32 igc_force_mac_fc(struct igc_hw *hw);
void igc_init_rx_addrs(struct igc_hw *hw, u16 rar_count);
void igc_init_rx_addrs(struct igc_hw *hw, u16 rar_count);
s32 igc_setup_link(struct igc_hw *hw);
s32 igc_setup_link(struct igc_hw *hw);
void igc_clear_hw_cntrs_base(struct igc_hw *hw);
void igc_clear_hw_cntrs_base(struct igc_hw *hw);
Loading