Commit 2909c3f7 authored by Alexander Duyck's avatar Alexander Duyck Committed by David S. Miller

igb: add support for the 82580 phy

This patch adds support for the phy included in the 82580 silicon family.
Signed-off-by: default avatarAlexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 164165da
......@@ -93,6 +93,7 @@ enum e1000_phy_type {
e1000_phy_gg82563,
e1000_phy_igp_3,
e1000_phy_ife,
e1000_phy_82580,
};
enum e1000_bus_type {
......
......@@ -420,6 +420,57 @@ s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
return ret_val;
}
/**
* igb_copper_link_setup_82580 - Setup 82580 PHY for copper link
* @hw: pointer to the HW structure
*
* Sets up Carrier-sense on Transmit and downshift values.
**/
s32 igb_copper_link_setup_82580(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data;
if (phy->reset_disable) {
ret_val = 0;
goto out;
}
if (phy->type == e1000_phy_82580) {
ret_val = hw->phy.ops.reset(hw);
if (ret_val) {
hw_dbg("Error resetting the PHY.\n");
goto out;
}
}
/* Enable CRS on TX. This must be set for half-duplex operation. */
ret_val = phy->ops.read_reg(hw, I82580_CFG_REG, &phy_data);
if (ret_val)
goto out;
phy_data |= I82580_CFG_ASSERT_CRS_ON_TX;
/* Enable downshift */
phy_data |= I82580_CFG_ENABLE_DOWNSHIFT;
ret_val = phy->ops.write_reg(hw, I82580_CFG_REG, phy_data);
if (ret_val)
goto out;
/* Set number of link attempts before downshift */
ret_val = phy->ops.read_reg(hw, I82580_CTRL_REG, &phy_data);
if (ret_val)
goto out;
phy_data &= ~I82580_CTRL_DOWNSHIFT_MASK;
ret_val = phy->ops.write_reg(hw, I82580_CTRL_REG, phy_data);
out:
return ret_val;
}
/**
* igb_copper_link_setup_m88 - Setup m88 PHY's for copper link
* @hw: pointer to the HW structure
......@@ -1888,3 +1939,194 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
return 0;
}
/**
* igb_check_polarity_82580 - Checks the polarity.
* @hw: pointer to the HW structure
*
* Success returns 0, Failure returns -E1000_ERR_PHY (-2)
*
* Polarity is determined based on the PHY specific status register.
**/
s32 igb_check_polarity_82580(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 data;
ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data);
if (!ret_val)
phy->cable_polarity = (data & I82580_PHY_STATUS2_REV_POLARITY)
? e1000_rev_polarity_reversed
: e1000_rev_polarity_normal;
return ret_val;
}
/**
* igb_phy_force_speed_duplex_82580 - Force speed/duplex for I82580 PHY
* @hw: pointer to the HW structure
*
* Calls the PHY setup function to force speed and duplex. Clears the
* auto-crossover to force MDI manually. Waits for link and returns
* successful if link up is successful, else -E1000_ERR_PHY (-2).
**/
s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data;
bool link;
ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
if (ret_val)
goto out;
igb_phy_force_speed_duplex_setup(hw, &phy_data);
ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
if (ret_val)
goto out;
/*
* Clear Auto-Crossover to force MDI manually. 82580 requires MDI
* forced whenever speed and duplex are forced.
*/
ret_val = phy->ops.read_reg(hw, I82580_PHY_CTRL_2, &phy_data);
if (ret_val)
goto out;
phy_data &= ~I82580_PHY_CTRL2_AUTO_MDIX;
phy_data &= ~I82580_PHY_CTRL2_FORCE_MDI_MDIX;
ret_val = phy->ops.write_reg(hw, I82580_PHY_CTRL_2, phy_data);
if (ret_val)
goto out;
hw_dbg("I82580_PHY_CTRL_2: %X\n", phy_data);
udelay(1);
if (phy->autoneg_wait_to_complete) {
hw_dbg("Waiting for forced speed/duplex link on 82580 phy\n");
ret_val = igb_phy_has_link(hw,
PHY_FORCE_LIMIT,
100000,
&link);
if (ret_val)
goto out;
if (!link)
hw_dbg("Link taking longer than expected.\n");
/* Try once more */
ret_val = igb_phy_has_link(hw,
PHY_FORCE_LIMIT,
100000,
&link);
if (ret_val)
goto out;
}
out:
return ret_val;
}
/**
* igb_get_phy_info_82580 - Retrieve I82580 PHY information
* @hw: pointer to the HW structure
*
* Read PHY status to determine if link is up. If link is up, then
* set/determine 10base-T extended distance and polarity correction. Read
* PHY port status to determine MDI/MDIx and speed. Based on the speed,
* determine on the cable length, local and remote receiver.
**/
s32 igb_get_phy_info_82580(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 data;
bool link;
ret_val = igb_phy_has_link(hw, 1, 0, &link);
if (ret_val)
goto out;
if (!link) {
hw_dbg("Phy info is only valid if link is up\n");
ret_val = -E1000_ERR_CONFIG;
goto out;
}
phy->polarity_correction = true;
ret_val = igb_check_polarity_82580(hw);
if (ret_val)
goto out;
ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data);
if (ret_val)
goto out;
phy->is_mdix = (data & I82580_PHY_STATUS2_MDIX) ? true : false;
if ((data & I82580_PHY_STATUS2_SPEED_MASK) ==
I82580_PHY_STATUS2_SPEED_1000MBPS) {
ret_val = hw->phy.ops.get_cable_length(hw);
if (ret_val)
goto out;
ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
if (ret_val)
goto out;
phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
? e1000_1000t_rx_status_ok
: e1000_1000t_rx_status_not_ok;
phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
? e1000_1000t_rx_status_ok
: e1000_1000t_rx_status_not_ok;
} else {
phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
phy->local_rx = e1000_1000t_rx_status_undefined;
phy->remote_rx = e1000_1000t_rx_status_undefined;
}
out:
return ret_val;
}
/**
* igb_get_cable_length_82580 - Determine cable length for 82580 PHY
* @hw: pointer to the HW structure
*
* Reads the diagnostic status register and verifies result is valid before
* placing it in the phy_cable_length field.
**/
s32 igb_get_cable_length_82580(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data, length;
ret_val = phy->ops.read_reg(hw, I82580_PHY_DIAG_STATUS, &phy_data);
if (ret_val)
goto out;
length = (phy_data & I82580_DSTATUS_CABLE_LENGTH) >>
I82580_DSTATUS_CABLE_LENGTH_SHIFT;
if (length == E1000_CABLE_LENGTH_UNDEFINED)
ret_val = -E1000_ERR_PHY;
phy->cable_length = length;
out:
return ret_val;
}
......@@ -63,6 +63,11 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_copper_link_setup_82580(struct e1000_hw *hw);
s32 igb_check_polarity_82580(struct e1000_hw *hw);
s32 igb_get_phy_info_82580(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
s32 igb_get_cable_length_82580(struct e1000_hw *hw);
/* IGP01E1000 Specific Registers */
#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */
......@@ -77,6 +82,33 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */
#define IGP01E1000_PSCFR_SMART_SPEED 0x0080
#define I82580_ADDR_REG 16
#define I82580_CFG_REG 22
#define I82580_CFG_ASSERT_CRS_ON_TX (1 << 15)
#define I82580_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */
#define I82580_CTRL_REG 23
#define I82580_CTRL_DOWNSHIFT_MASK (7 << 10)
/* 82580 specific PHY registers */
#define I82580_PHY_CTRL_2 18
#define I82580_PHY_LBK_CTRL 19
#define I82580_PHY_STATUS_2 26
#define I82580_PHY_DIAG_STATUS 31
/* I82580 PHY Status 2 */
#define I82580_PHY_STATUS2_REV_POLARITY 0x0400
#define I82580_PHY_STATUS2_MDIX 0x0800
#define I82580_PHY_STATUS2_SPEED_MASK 0x0300
#define I82580_PHY_STATUS2_SPEED_1000MBPS 0x0200
#define I82580_PHY_STATUS2_SPEED_100MBPS 0x0100
/* I82580 PHY Control 2 */
#define I82580_PHY_CTRL2_AUTO_MDIX 0x0400
#define I82580_PHY_CTRL2_FORCE_MDI_MDIX 0x0200
/* I82580 PHY Diagnostics Status */
#define I82580_DSTATUS_CABLE_LENGTH 0x03FC
#define I82580_DSTATUS_CABLE_LENGTH_SHIFT 2
/* Enable flexible speed on link-up */
#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */
#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment