igb: Add Support for new i210/i211 devices.

This patch adds new initialization functions and device support
for i210 and i211 devices.

Signed-off-by: Carolyn Wyborny <carolyn.wyborny@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Carolyn Wyborny 2012-04-06 23:25:19 +00:00 committed by Jeff Kirsher
parent da02cde1c1
commit f96a8a0b78
16 changed files with 1205 additions and 110 deletions

View file

@ -35,6 +35,7 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw);
static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw,
u16 *phy_ctrl);
static s32 igb_wait_autoneg(struct e1000_hw *hw);
static s32 igb_set_master_slave_mode(struct e1000_hw *hw);
/* Cable length tables */
static const u16 e1000_m88_cable_length_table[] =
@ -570,6 +571,11 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
hw_dbg("Error committing the PHY changes\n");
goto out;
}
if (phy->type == e1000_phy_i210) {
ret_val = igb_set_master_slave_mode(hw);
if (ret_val)
return ret_val;
}
out:
return ret_val;
@ -1213,12 +1219,22 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out;
if (!link) {
if (hw->phy.type != e1000_phy_m88 ||
hw->phy.id == I347AT4_E_PHY_ID ||
hw->phy.id == M88E1112_E_PHY_ID) {
hw_dbg("Link taking longer than expected.\n");
} else {
bool reset_dsp = true;
switch (hw->phy.id) {
case I347AT4_E_PHY_ID:
case M88E1112_E_PHY_ID:
case I210_I_PHY_ID:
reset_dsp = false;
break;
default:
if (hw->phy.type != e1000_phy_m88)
reset_dsp = false;
break;
}
if (!reset_dsp)
hw_dbg("Link taking longer than expected.\n");
else {
/*
* We didn't get link.
* Reset the DSP and cross our fingers.
@ -1243,7 +1259,8 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
if (hw->phy.type != e1000_phy_m88 ||
hw->phy.id == I347AT4_E_PHY_ID ||
hw->phy.id == M88E1112_E_PHY_ID)
hw->phy.id == M88E1112_E_PHY_ID ||
hw->phy.id == I210_I_PHY_ID)
goto out;
ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
@ -1441,6 +1458,7 @@ s32 igb_check_downshift(struct e1000_hw *hw)
u16 phy_data, offset, mask;
switch (phy->type) {
case e1000_phy_i210:
case e1000_phy_m88:
case e1000_phy_gg82563:
offset = M88E1000_PHY_SPEC_STATUS;
@ -1476,7 +1494,7 @@ out:
*
* Polarity is determined based on the PHY specific status register.
**/
static s32 igb_check_polarity_m88(struct e1000_hw *hw)
s32 igb_check_polarity_m88(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
@ -1665,6 +1683,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
u16 phy_data, phy_data2, index, default_page, is_cm;
switch (hw->phy.id) {
case I210_I_PHY_ID:
case I347AT4_E_PHY_ID:
/* Remember the original page select and set it to 7 */
ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
@ -2129,10 +2148,16 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
void igb_power_up_phy_copper(struct e1000_hw *hw)
{
u16 mii_reg = 0;
u16 power_reg = 0;
/* The PHY will retain its settings across a power down/up cycle */
hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
mii_reg &= ~MII_CR_POWER_DOWN;
if (hw->phy.type == e1000_phy_i210) {
hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
power_reg &= ~GS40G_CS_POWER_DOWN;
hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
}
hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
}
@ -2146,10 +2171,18 @@ void igb_power_up_phy_copper(struct e1000_hw *hw)
void igb_power_down_phy_copper(struct e1000_hw *hw)
{
u16 mii_reg = 0;
u16 power_reg = 0;
/* The PHY will retain its settings across a power down/up cycle */
hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN;
/* i210 Phy requires an additional bit for power up/down */
if (hw->phy.type == e1000_phy_i210) {
hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
power_reg |= GS40G_CS_POWER_DOWN;
hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
}
hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
msleep(1);
}
@ -2345,3 +2378,103 @@ s32 igb_get_cable_length_82580(struct e1000_hw *hw)
out:
return ret_val;
}
/**
* igb_write_phy_reg_gs40g - Write GS40G PHY register
* @hw: pointer to the HW structure
* @offset: lower half is register offset to write to
* upper half is page to use.
* @data: data to write at register offset
*
* Acquires semaphore, if necessary, then writes the data to PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data)
{
s32 ret_val;
u16 page = offset >> GS40G_PAGE_SHIFT;
offset = offset & GS40G_OFFSET_MASK;
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
if (ret_val)
goto release;
ret_val = igb_write_phy_reg_mdic(hw, offset, data);
release:
hw->phy.ops.release(hw);
return ret_val;
}
/**
* igb_read_phy_reg_gs40g - Read GS40G PHY register
* @hw: pointer to the HW structure
* @offset: lower half is register offset to read to
* upper half is page to use.
* @data: data to read at register offset
*
* Acquires semaphore, if necessary, then reads the data in the PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data)
{
s32 ret_val;
u16 page = offset >> GS40G_PAGE_SHIFT;
offset = offset & GS40G_OFFSET_MASK;
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
if (ret_val)
goto release;
ret_val = igb_read_phy_reg_mdic(hw, offset, data);
release:
hw->phy.ops.release(hw);
return ret_val;
}
/**
* igb_set_master_slave_mode - Setup PHY for Master/slave mode
* @hw: pointer to the HW structure
*
* Sets up Master/slave mode
**/
static s32 igb_set_master_slave_mode(struct e1000_hw *hw)
{
s32 ret_val;
u16 phy_data;
/* Resolve Master/Slave mode */
ret_val = hw->phy.ops.read_reg(hw, PHY_1000T_CTRL, &phy_data);
if (ret_val)
return ret_val;
/* load defaults for future use */
hw->phy.original_ms_type = (phy_data & CR_1000T_MS_ENABLE) ?
((phy_data & CR_1000T_MS_VALUE) ?
e1000_ms_force_master :
e1000_ms_force_slave) : e1000_ms_auto;
switch (hw->phy.ms_type) {
case e1000_ms_force_master:
phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
break;
case e1000_ms_force_slave:
phy_data |= CR_1000T_MS_ENABLE;
phy_data &= ~(CR_1000T_MS_VALUE);
break;
case e1000_ms_auto:
phy_data &= ~CR_1000T_MS_ENABLE;
/* fall-through */
default:
break;
}
return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data);
}