Skip to content

Commit

Permalink
igb: add support for the 82580 phy
Browse files Browse the repository at this point in the history
This patch adds support for the phy included in the 82580 silicon family.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alexander Duyck authored and David S. Miller committed Nov 20, 2009
1 parent 164165d commit 2909c3f
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/igb/e1000_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
242 changes: 242 additions & 0 deletions drivers/net/igb/e1000_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
32 changes: 32 additions & 0 deletions drivers/net/igb/e1000_phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down

0 comments on commit 2909c3f

Please sign in to comment.