mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 11:46:19 +00:00
e1000e: rework disable K1 at 1000Mbps for 82577/82578
This patch reworks a previous workaround (commit 7d3cabbcc
) 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. The original version of this
workaround was found to be incomplete.
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
f523d2114e
commit
1d5846b921
5 changed files with 190 additions and 28 deletions
|
@ -76,6 +76,7 @@
|
|||
/* Extended Device Control */
|
||||
#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Definable Pin 7 */
|
||||
#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */
|
||||
#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */
|
||||
#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */
|
||||
#define E1000_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clock Gating */
|
||||
#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
|
||||
|
|
|
@ -141,6 +141,20 @@ struct e1000_info;
|
|||
#define HV_TNCRS_UPPER PHY_REG(778, 29) /* Transmit with no CRS */
|
||||
#define HV_TNCRS_LOWER PHY_REG(778, 30)
|
||||
|
||||
/* BM PHY Copper Specific Status */
|
||||
#define BM_CS_STATUS 17
|
||||
#define BM_CS_STATUS_LINK_UP 0x0400
|
||||
#define BM_CS_STATUS_RESOLVED 0x0800
|
||||
#define BM_CS_STATUS_SPEED_MASK 0xC000
|
||||
#define BM_CS_STATUS_SPEED_1000 0x8000
|
||||
|
||||
/* 82577 Mobile Phy Status Register */
|
||||
#define HV_M_STATUS 26
|
||||
#define HV_M_STATUS_AUTONEG_COMPLETE 0x1000
|
||||
#define HV_M_STATUS_SPEED_MASK 0x0300
|
||||
#define HV_M_STATUS_SPEED_1000 0x0200
|
||||
#define HV_M_STATUS_LINK_UP 0x0040
|
||||
|
||||
enum e1000_boards {
|
||||
board_82571,
|
||||
board_82572,
|
||||
|
|
|
@ -903,6 +903,7 @@ struct e1000_shadow_ram {
|
|||
struct e1000_dev_spec_ich8lan {
|
||||
bool kmrn_lock_loss_workaround_enabled;
|
||||
struct e1000_shadow_ram shadow_ram[E1000_ICH8_SHADOW_RAM_WORDS];
|
||||
bool nvm_k1_enabled;
|
||||
};
|
||||
|
||||
struct e1000_hw {
|
||||
|
|
|
@ -140,6 +140,9 @@
|
|||
#define HV_OEM_BITS_GBE_DIS 0x0040 /* Gigabit Disable */
|
||||
#define HV_OEM_BITS_RESTART_AN 0x0400 /* Restart Auto-negotiation */
|
||||
|
||||
#define E1000_NVM_K1_CONFIG 0x1B /* NVM K1 Config Word */
|
||||
#define E1000_NVM_K1_ENABLE 0x1 /* NVM Enable K1 bit */
|
||||
|
||||
/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
|
||||
/* Offset 04h HSFSTS */
|
||||
union ich8_hws_flash_status {
|
||||
|
@ -220,6 +223,8 @@ static s32 e1000_led_on_pchlan(struct e1000_hw *hw);
|
|||
static s32 e1000_led_off_pchlan(struct e1000_hw *hw);
|
||||
static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active);
|
||||
static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
|
||||
static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
|
||||
static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
|
||||
|
||||
static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
|
||||
{
|
||||
|
@ -495,14 +500,6 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
|
|||
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
|
||||
|
@ -512,6 +509,12 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
|
|||
if (ret_val)
|
||||
goto out;
|
||||
|
||||
if (hw->mac.type == e1000_pchlan) {
|
||||
ret_val = e1000_k1_gig_workaround_hv(hw, link);
|
||||
if (ret_val)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!link)
|
||||
goto out; /* No link detected */
|
||||
|
||||
|
@ -928,6 +931,141 @@ out:
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* e1000_k1_gig_workaround_hv - K1 Si workaround
|
||||
* @hw: pointer to the HW structure
|
||||
* @link: link up bool flag
|
||||
*
|
||||
* If K1 is enabled for 1Gbps, the MAC might stall when transitioning
|
||||
* from a lower speed. This workaround disables K1 whenever link is at 1Gig
|
||||
* If link is down, the function will restore the default K1 setting located
|
||||
* in the NVM.
|
||||
**/
|
||||
static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
|
||||
{
|
||||
s32 ret_val = 0;
|
||||
u16 status_reg = 0;
|
||||
bool k1_enable = hw->dev_spec.ich8lan.nvm_k1_enabled;
|
||||
|
||||
if (hw->mac.type != e1000_pchlan)
|
||||
goto out;
|
||||
|
||||
/* Wrap the whole flow with the sw flag */
|
||||
ret_val = hw->phy.ops.acquire_phy(hw);
|
||||
if (ret_val)
|
||||
goto out;
|
||||
|
||||
/* Disable K1 when link is 1Gbps, otherwise use the NVM setting */
|
||||
if (link) {
|
||||
if (hw->phy.type == e1000_phy_82578) {
|
||||
ret_val = hw->phy.ops.read_phy_reg_locked(hw,
|
||||
BM_CS_STATUS,
|
||||
&status_reg);
|
||||
if (ret_val)
|
||||
goto release;
|
||||
|
||||
status_reg &= BM_CS_STATUS_LINK_UP |
|
||||
BM_CS_STATUS_RESOLVED |
|
||||
BM_CS_STATUS_SPEED_MASK;
|
||||
|
||||
if (status_reg == (BM_CS_STATUS_LINK_UP |
|
||||
BM_CS_STATUS_RESOLVED |
|
||||
BM_CS_STATUS_SPEED_1000))
|
||||
k1_enable = false;
|
||||
}
|
||||
|
||||
if (hw->phy.type == e1000_phy_82577) {
|
||||
ret_val = hw->phy.ops.read_phy_reg_locked(hw,
|
||||
HV_M_STATUS,
|
||||
&status_reg);
|
||||
if (ret_val)
|
||||
goto release;
|
||||
|
||||
status_reg &= HV_M_STATUS_LINK_UP |
|
||||
HV_M_STATUS_AUTONEG_COMPLETE |
|
||||
HV_M_STATUS_SPEED_MASK;
|
||||
|
||||
if (status_reg == (HV_M_STATUS_LINK_UP |
|
||||
HV_M_STATUS_AUTONEG_COMPLETE |
|
||||
HV_M_STATUS_SPEED_1000))
|
||||
k1_enable = false;
|
||||
}
|
||||
|
||||
/* Link stall fix for link up */
|
||||
ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19),
|
||||
0x0100);
|
||||
if (ret_val)
|
||||
goto release;
|
||||
|
||||
} else {
|
||||
/* Link stall fix for link down */
|
||||
ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19),
|
||||
0x4100);
|
||||
if (ret_val)
|
||||
goto release;
|
||||
}
|
||||
|
||||
ret_val = e1000_configure_k1_ich8lan(hw, k1_enable);
|
||||
|
||||
release:
|
||||
hw->phy.ops.release_phy(hw);
|
||||
out:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* e1000_configure_k1_ich8lan - Configure K1 power state
|
||||
* @hw: pointer to the HW structure
|
||||
* @enable: K1 state to configure
|
||||
*
|
||||
* Configure the K1 power state based on the provided parameter.
|
||||
* Assumes semaphore already acquired.
|
||||
*
|
||||
* Success returns 0, Failure returns -E1000_ERR_PHY (-2)
|
||||
**/
|
||||
static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
|
||||
{
|
||||
s32 ret_val = 0;
|
||||
u32 ctrl_reg = 0;
|
||||
u32 ctrl_ext = 0;
|
||||
u32 reg = 0;
|
||||
u16 kmrn_reg = 0;
|
||||
|
||||
ret_val = e1000e_read_kmrn_reg_locked(hw,
|
||||
E1000_KMRNCTRLSTA_K1_CONFIG,
|
||||
&kmrn_reg);
|
||||
if (ret_val)
|
||||
goto out;
|
||||
|
||||
if (k1_enable)
|
||||
kmrn_reg |= E1000_KMRNCTRLSTA_K1_ENABLE;
|
||||
else
|
||||
kmrn_reg &= ~E1000_KMRNCTRLSTA_K1_ENABLE;
|
||||
|
||||
ret_val = e1000e_write_kmrn_reg_locked(hw,
|
||||
E1000_KMRNCTRLSTA_K1_CONFIG,
|
||||
kmrn_reg);
|
||||
if (ret_val)
|
||||
goto out;
|
||||
|
||||
udelay(20);
|
||||
ctrl_ext = er32(CTRL_EXT);
|
||||
ctrl_reg = er32(CTRL);
|
||||
|
||||
reg = ctrl_reg & ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
|
||||
reg |= E1000_CTRL_FRCSPD;
|
||||
ew32(CTRL, reg);
|
||||
|
||||
ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
|
||||
udelay(20);
|
||||
ew32(CTRL, ctrl_reg);
|
||||
ew32(CTRL_EXT, ctrl_ext);
|
||||
udelay(20);
|
||||
|
||||
out:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* e1000_oem_bits_config_ich8lan - SW-based LCD Configuration
|
||||
* @hw: pointer to the HW structure
|
||||
|
@ -1030,10 +1168,20 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
|
|||
ret_val = hw->phy.ops.acquire_phy(hw);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
hw->phy.addr = 1;
|
||||
e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
|
||||
ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
|
||||
if (ret_val)
|
||||
goto out;
|
||||
hw->phy.ops.release_phy(hw);
|
||||
|
||||
/*
|
||||
* Configure the K1 Si workaround during phy reset assuming there is
|
||||
* link so that it disables K1 if link is in 1Gbps.
|
||||
*/
|
||||
ret_val = e1000_k1_gig_workaround_hv(hw, true);
|
||||
|
||||
out:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
@ -2435,6 +2583,7 @@ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw)
|
|||
**/
|
||||
static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
|
||||
{
|
||||
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
|
||||
u16 reg;
|
||||
u32 ctrl, icr, kab;
|
||||
s32 ret_val;
|
||||
|
@ -2470,6 +2619,18 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
|
|||
ew32(PBS, E1000_PBS_16K);
|
||||
}
|
||||
|
||||
if (hw->mac.type == e1000_pchlan) {
|
||||
/* Save the NVM K1 bit setting*/
|
||||
ret_val = e1000_read_nvm(hw, E1000_NVM_K1_CONFIG, 1, ®);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
if (reg & E1000_NVM_K1_ENABLE)
|
||||
dev_spec->nvm_k1_enabled = true;
|
||||
else
|
||||
dev_spec->nvm_k1_enabled = false;
|
||||
}
|
||||
|
||||
ctrl = er32(CTRL);
|
||||
|
||||
if (!e1000_check_reset_block(hw)) {
|
||||
|
@ -2847,14 +3008,6 @@ static s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed,
|
|||
if (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) &&
|
||||
(hw->phy.type == e1000_phy_igp_3) &&
|
||||
(*speed == SPEED_1000)) {
|
||||
|
|
|
@ -95,13 +95,6 @@ static const u16 e1000_igp_2_cable_length_table[] =
|
|||
/* BM PHY Copper Specific Control 1 */
|
||||
#define BM_CS_CTRL1 16
|
||||
|
||||
/* BM PHY Copper Specific Status */
|
||||
#define BM_CS_STATUS 17
|
||||
#define BM_CS_STATUS_LINK_UP 0x0400
|
||||
#define BM_CS_STATUS_RESOLVED 0x0800
|
||||
#define BM_CS_STATUS_SPEED_MASK 0xC000
|
||||
#define BM_CS_STATUS_SPEED_1000 0x8000
|
||||
|
||||
#define HV_MUX_DATA_CTRL PHY_REG(776, 16)
|
||||
#define HV_MUX_DATA_CTRL_GEN_TO_MAC 0x0400
|
||||
#define HV_MUX_DATA_CTRL_FORCE_SPEED 0x0004
|
||||
|
@ -563,7 +556,7 @@ s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data)
|
|||
}
|
||||
|
||||
/**
|
||||
* e1000_read_kmrn_reg_locked - Read kumeran register
|
||||
* e1000e_read_kmrn_reg_locked - Read kumeran register
|
||||
* @hw: pointer to the HW structure
|
||||
* @offset: register offset to be read
|
||||
* @data: pointer to the read data
|
||||
|
@ -572,7 +565,7 @@ s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data)
|
|||
* information retrieved is stored in data.
|
||||
* Assumes semaphore already acquired.
|
||||
**/
|
||||
s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
|
||||
s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
|
||||
{
|
||||
return __e1000_read_kmrn_reg(hw, offset, data, true);
|
||||
}
|
||||
|
@ -631,7 +624,7 @@ s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data)
|
|||
}
|
||||
|
||||
/**
|
||||
* e1000_write_kmrn_reg_locked - Write kumeran register
|
||||
* e1000e_write_kmrn_reg_locked - Write kumeran register
|
||||
* @hw: pointer to the HW structure
|
||||
* @offset: register offset to write to
|
||||
* @data: data to write at register offset
|
||||
|
@ -639,7 +632,7 @@ s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data)
|
|||
* Write the data to PHY register at the offset using the kumeran interface.
|
||||
* Assumes semaphore already acquired.
|
||||
**/
|
||||
s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data)
|
||||
s32 e1000e_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data)
|
||||
{
|
||||
return __e1000_write_kmrn_reg(hw, offset, data, true);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue