e1000e: disable K1 at 1000Mbps for 82577/82578
This workaround is required for an issue in hardware where noise on the interconnect between the MAC and PHY could be generated by a lower power mode (K1) at 1000Mbps resulting in bad packets. Disable K1 while at 1000 Mbps but keep it enabled for 10/100Mbps and when the cable is disconnected. Signed-off-by: Bruce Allan <bruce.w.allan@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
					parent
					
						
							
								906e8d9792
							
						
					
				
			
			
				commit
				
					
						7d3cabbcc8
					
				
			
		
					 3 changed files with 110 additions and 7 deletions
				
			
		|  | @ -215,6 +215,7 @@ enum e1e_registers { | ||||||
| 	E1000_SWSM      = 0x05B50, /* SW Semaphore */ | 	E1000_SWSM      = 0x05B50, /* SW Semaphore */ | ||||||
| 	E1000_FWSM      = 0x05B54, /* FW Semaphore */ | 	E1000_FWSM      = 0x05B54, /* FW Semaphore */ | ||||||
| 	E1000_SWSM2     = 0x05B58, /* Driver-only SW semaphore */ | 	E1000_SWSM2     = 0x05B58, /* Driver-only SW semaphore */ | ||||||
|  | 	E1000_CRC_OFFSET = 0x05F50, /* CRC Offset register */ | ||||||
| 	E1000_HICR      = 0x08F00, /* Host Interface Control */ | 	E1000_HICR      = 0x08F00, /* Host Interface Control */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -302,6 +303,9 @@ enum e1e_registers { | ||||||
| #define E1000_KMRNCTRLSTA_REN		0x00200000 | #define E1000_KMRNCTRLSTA_REN		0x00200000 | ||||||
| #define E1000_KMRNCTRLSTA_DIAG_OFFSET	0x3    /* Kumeran Diagnostic */ | #define E1000_KMRNCTRLSTA_DIAG_OFFSET	0x3    /* Kumeran Diagnostic */ | ||||||
| #define E1000_KMRNCTRLSTA_DIAG_NELPBK	0x1000 /* Nearend Loopback mode */ | #define E1000_KMRNCTRLSTA_DIAG_NELPBK	0x1000 /* Nearend Loopback mode */ | ||||||
|  | #define E1000_KMRNCTRLSTA_K1_CONFIG	0x7 | ||||||
|  | #define E1000_KMRNCTRLSTA_K1_ENABLE	0x140E | ||||||
|  | #define E1000_KMRNCTRLSTA_K1_DISABLE	0x1400 | ||||||
| 
 | 
 | ||||||
| #define IFE_PHY_EXTENDED_STATUS_CONTROL	0x10 | #define IFE_PHY_EXTENDED_STATUS_CONTROL	0x10 | ||||||
| #define IFE_PHY_SPECIAL_CONTROL		0x11 /* 100BaseTx PHY Special Control */ | #define IFE_PHY_SPECIAL_CONTROL		0x11 /* 100BaseTx PHY Special Control */ | ||||||
|  |  | ||||||
|  | @ -461,6 +461,95 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  *  e1000_check_for_copper_link_ich8lan - Check for link (Copper) | ||||||
|  |  *  @hw: pointer to the HW structure | ||||||
|  |  * | ||||||
|  |  *  Checks to see of the link status of the hardware has changed.  If a | ||||||
|  |  *  change in link status has been detected, then we read the PHY registers | ||||||
|  |  *  to get the current speed/duplex if link exists. | ||||||
|  |  **/ | ||||||
|  | static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) | ||||||
|  | { | ||||||
|  | 	struct e1000_mac_info *mac = &hw->mac; | ||||||
|  | 	s32 ret_val; | ||||||
|  | 	bool link; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We only want to go out to the PHY registers to see if Auto-Neg | ||||||
|  | 	 * has completed and/or if our link status has changed.  The | ||||||
|  | 	 * get_link_status flag is set upon receiving a Link Status | ||||||
|  | 	 * Change or Rx Sequence Error interrupt. | ||||||
|  | 	 */ | ||||||
|  | 	if (!mac->get_link_status) { | ||||||
|  | 		ret_val = 0; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (hw->mac.type == e1000_pchlan) { | ||||||
|  | 		ret_val = e1000e_write_kmrn_reg(hw, | ||||||
|  | 		                                   E1000_KMRNCTRLSTA_K1_CONFIG, | ||||||
|  | 		                                   E1000_KMRNCTRLSTA_K1_ENABLE); | ||||||
|  | 		if (ret_val) | ||||||
|  | 			goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * First we want to see if the MII Status Register reports | ||||||
|  | 	 * link.  If so, then we want to get the current speed/duplex | ||||||
|  | 	 * of the PHY. | ||||||
|  | 	 */ | ||||||
|  | 	ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link); | ||||||
|  | 	if (ret_val) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	if (!link) | ||||||
|  | 		goto out; /* No link detected */ | ||||||
|  | 
 | ||||||
|  | 	mac->get_link_status = false; | ||||||
|  | 
 | ||||||
|  | 	if (hw->phy.type == e1000_phy_82578) { | ||||||
|  | 		ret_val = e1000_link_stall_workaround_hv(hw); | ||||||
|  | 		if (ret_val) | ||||||
|  | 			goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Check if there was DownShift, must be checked | ||||||
|  | 	 * immediately after link-up | ||||||
|  | 	 */ | ||||||
|  | 	e1000e_check_downshift(hw); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * If we are forcing speed/duplex, then we simply return since | ||||||
|  | 	 * we have already determined whether we have link or not. | ||||||
|  | 	 */ | ||||||
|  | 	if (!mac->autoneg) { | ||||||
|  | 		ret_val = -E1000_ERR_CONFIG; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Auto-Neg is enabled.  Auto Speed Detection takes care | ||||||
|  | 	 * of MAC speed/duplex configuration.  So we only need to | ||||||
|  | 	 * configure Collision Distance in the MAC. | ||||||
|  | 	 */ | ||||||
|  | 	e1000e_config_collision_dist(hw); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Configure Flow Control now that Auto-Neg has completed. | ||||||
|  | 	 * First, we need to restore the desired flow control | ||||||
|  | 	 * settings because we may have had to re-autoneg with a | ||||||
|  | 	 * different link partner. | ||||||
|  | 	 */ | ||||||
|  | 	ret_val = e1000e_config_fc_after_link_up(hw); | ||||||
|  | 	if (ret_val) | ||||||
|  | 		hw_dbg(hw, "Error configuring flow control\n"); | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	return ret_val; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) | static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) | ||||||
| { | { | ||||||
| 	struct e1000_hw *hw = &adapter->hw; | 	struct e1000_hw *hw = &adapter->hw; | ||||||
|  | @ -2224,6 +2313,14 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * For PCH, this write will make sure that any noise | ||||||
|  | 	 * will be detected as a CRC error and be dropped rather than show up | ||||||
|  | 	 * as a bad packet to the DMA engine. | ||||||
|  | 	 */ | ||||||
|  | 	if (hw->mac.type == e1000_pchlan) | ||||||
|  | 		ew32(CRC_OFFSET, 0x65656565); | ||||||
|  | 
 | ||||||
| 	ew32(IMC, 0xffffffff); | 	ew32(IMC, 0xffffffff); | ||||||
| 	icr = er32(ICR); | 	icr = er32(ICR); | ||||||
| 
 | 
 | ||||||
|  | @ -2538,6 +2635,14 @@ static s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed, | ||||||
| 	if (ret_val) | 	if (ret_val) | ||||||
| 		return ret_val; | 		return ret_val; | ||||||
| 
 | 
 | ||||||
|  | 	if ((hw->mac.type == e1000_pchlan) && (*speed == SPEED_1000)) { | ||||||
|  | 		ret_val = e1000e_write_kmrn_reg(hw, | ||||||
|  | 		                                  E1000_KMRNCTRLSTA_K1_CONFIG, | ||||||
|  | 		                                  E1000_KMRNCTRLSTA_K1_DISABLE); | ||||||
|  | 		if (ret_val) | ||||||
|  | 			return ret_val; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if ((hw->mac.type == e1000_ich8lan) && | 	if ((hw->mac.type == e1000_ich8lan) && | ||||||
| 	    (hw->phy.type == e1000_phy_igp_3) && | 	    (hw->phy.type == e1000_phy_igp_3) && | ||||||
| 	    (*speed == SPEED_1000)) { | 	    (*speed == SPEED_1000)) { | ||||||
|  | @ -2984,7 +3089,7 @@ static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw) | ||||||
| static struct e1000_mac_operations ich8_mac_ops = { | static struct e1000_mac_operations ich8_mac_ops = { | ||||||
| 	.id_led_init		= e1000e_id_led_init, | 	.id_led_init		= e1000e_id_led_init, | ||||||
| 	.check_mng_mode		= e1000_check_mng_mode_ich8lan, | 	.check_mng_mode		= e1000_check_mng_mode_ich8lan, | ||||||
| 	.check_for_link		= e1000e_check_for_copper_link, | 	.check_for_link		= e1000_check_for_copper_link_ich8lan, | ||||||
| 	/* cleanup_led dependent on mac type */ | 	/* cleanup_led dependent on mac type */ | ||||||
| 	.clear_hw_cntrs		= e1000_clear_hw_cntrs_ich8lan, | 	.clear_hw_cntrs		= e1000_clear_hw_cntrs_ich8lan, | ||||||
| 	.get_bus_info		= e1000_get_bus_info_ich8lan, | 	.get_bus_info		= e1000_get_bus_info_ich8lan, | ||||||
|  |  | ||||||
|  | @ -378,12 +378,6 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) | ||||||
| 
 | 
 | ||||||
| 	mac->get_link_status = 0; | 	mac->get_link_status = 0; | ||||||
| 
 | 
 | ||||||
| 	if (hw->phy.type == e1000_phy_82578) { |  | ||||||
| 		ret_val = e1000_link_stall_workaround_hv(hw); |  | ||||||
| 		if (ret_val) |  | ||||||
| 			return ret_val; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Check if there was DownShift, must be checked | 	 * Check if there was DownShift, must be checked | ||||||
| 	 * immediately after link-up | 	 * immediately after link-up | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Bruce Allan
				Bruce Allan