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

Commit 23a2d1b2 authored by Dave Graham's avatar Dave Graham Committed by David S. Miller
Browse files

e1000e: Fixes possible phy corrupton on 82571 designs.



Phy corruption has been observed on 2-port 82571 adapters, and is root-caused
to lack of synchronization between the 2 driver instances, which conflict
when attempting to access the phy via the single MDIC register.
A semaphore exists for this purpose, and is now used on these designs. Because
PXE &/or EFI boot code (which we cannot expect to be built with this fix) may
leave the inter-instance semaphore in an invalid initial state when the driver
first loads, this fix also includes a one-time (per driver load) fix-up of the
semaphore initial state.

Signed-off-by: default avatardave graham <david.graham@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8459464f
Loading
Loading
Loading
Loading
+79 −7
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ static s32 e1000_setup_link_82571(struct e1000_hw *hw);
static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw);
static bool e1000_check_mng_mode_82574(struct e1000_hw *hw);
static s32 e1000_led_on_82574(struct e1000_hw *hw);
static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw);

/**
 *  e1000_init_phy_params_82571 - Init PHY func ptrs.
@@ -212,6 +213,9 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
	struct e1000_hw *hw = &adapter->hw;
	struct e1000_mac_info *mac = &hw->mac;
	struct e1000_mac_operations *func = &mac->ops;
	u32 swsm = 0;
	u32 swsm2 = 0;
	bool force_clear_smbi = false;

	/* Set media type */
	switch (adapter->pdev->device) {
@@ -276,6 +280,50 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
		break;
	}

	/*
	 * Ensure that the inter-port SWSM.SMBI lock bit is clear before
	 * first NVM or PHY acess. This should be done for single-port
	 * devices, and for one port only on dual-port devices so that
	 * for those devices we can still use the SMBI lock to synchronize
	 * inter-port accesses to the PHY & NVM.
	 */
	switch (hw->mac.type) {
	case e1000_82571:
	case e1000_82572:
		swsm2 = er32(SWSM2);

		if (!(swsm2 & E1000_SWSM2_LOCK)) {
			/* Only do this for the first interface on this card */
			ew32(SWSM2,
			    swsm2 | E1000_SWSM2_LOCK);
			force_clear_smbi = true;
		} else
			force_clear_smbi = false;
		break;
	default:
		force_clear_smbi = true;
		break;
	}

	if (force_clear_smbi) {
		/* Make sure SWSM.SMBI is clear */
		swsm = er32(SWSM);
		if (swsm & E1000_SWSM_SMBI) {
			/* This bit should not be set on a first interface, and
			 * indicates that the bootagent or EFI code has
			 * improperly left this bit enabled
			 */
			hw_dbg(hw, "Please update your 82571 Bootagent\n");
		}
		ew32(SWSM, swsm & ~E1000_SWSM_SMBI);
	}

	/*
	 * Initialze device specific counter of SMBI acquisition
	 * timeouts.
	 */
	 hw->dev_spec.e82571.smb_counter = 0;

	return 0;
}

@@ -413,11 +461,37 @@ static s32 e1000_get_phy_id_82571(struct e1000_hw *hw)
static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
{
	u32 swsm;
	s32 timeout = hw->nvm.word_size + 1;
	s32 sw_timeout = hw->nvm.word_size + 1;
	s32 fw_timeout = hw->nvm.word_size + 1;
	s32 i = 0;

	/*
	 * If we have timedout 3 times on trying to acquire
	 * the inter-port SMBI semaphore, there is old code
	 * operating on the other port, and it is not
	 * releasing SMBI. Modify the number of times that
	 * we try for the semaphore to interwork with this
	 * older code.
	 */
	if (hw->dev_spec.e82571.smb_counter > 2)
		sw_timeout = 1;

	/* Get the SW semaphore */
	while (i < sw_timeout) {
		swsm = er32(SWSM);
		if (!(swsm & E1000_SWSM_SMBI))
			break;

		udelay(50);
		i++;
	}

	if (i == sw_timeout) {
		hw_dbg(hw, "Driver can't access device - SMBI bit is set.\n");
		hw->dev_spec.e82571.smb_counter++;
	}
	/* Get the FW semaphore. */
	for (i = 0; i < timeout; i++) {
	for (i = 0; i < fw_timeout; i++) {
		swsm = er32(SWSM);
		ew32(SWSM, swsm | E1000_SWSM_SWESMBI);

@@ -428,9 +502,9 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
		udelay(50);
	}

	if (i == timeout) {
	if (i == fw_timeout) {
		/* Release semaphores */
		e1000e_put_hw_semaphore(hw);
		e1000_put_hw_semaphore_82571(hw);
		hw_dbg(hw, "Driver can't access the NVM\n");
		return -E1000_ERR_NVM;
	}
@@ -449,9 +523,7 @@ static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
	u32 swsm;

	swsm = er32(SWSM);

	swsm &= ~E1000_SWSM_SWESMBI;

	swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
	ew32(SWSM, swsm);
}

+2 −0
Original line number Diff line number Diff line
@@ -376,6 +376,8 @@
#define E1000_SWSM_SWESMBI      0x00000002 /* FW Semaphore bit */
#define E1000_SWSM_DRV_LOAD     0x00000008 /* Driver Loaded Bit */

#define E1000_SWSM2_LOCK        0x00000002 /* Secondary driver semaphore bit */

/* Interrupt Cause Read */
#define E1000_ICR_TXDW          0x00000001 /* Transmit desc written back */
#define E1000_ICR_LSC           0x00000004 /* Link Status Change */
+2 −0
Original line number Diff line number Diff line
@@ -214,6 +214,7 @@ enum e1e_registers {
	E1000_FACTPS    = 0x05B30, /* Function Active and Power State to MNG */
	E1000_SWSM      = 0x05B50, /* SW Semaphore */
	E1000_FWSM      = 0x05B54, /* FW Semaphore */
	E1000_SWSM2     = 0x05B58, /* Driver-only SW semaphore */
	E1000_HICR      = 0x08F00, /* Host Interface Control */
};

@@ -883,6 +884,7 @@ struct e1000_fc_info {
struct e1000_dev_spec_82571 {
	bool laa_is_present;
	bool alt_mac_addr_is_present;
	u32 smb_counter;
};

struct e1000_shadow_ram {