Skip to content

Commit

Permalink
igb: Add support for DH89xxCC
Browse files Browse the repository at this point in the history
This patch adds support for the Intel(r) DH89xxCC series. The new
device will be using Intel(r) i347-AT4 and Marvell(r) M88E1322 and
M88E1112 PHYs. Support for these devices has also been added here.

Signed-off-by: Joseph Gasparakis <joseph.gasparakis@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Joseph Gasparakis authored and David S. Miller committed Sep 23, 2010
1 parent d85b900 commit 308fb39
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 14 deletions.
18 changes: 16 additions & 2 deletions drivers/net/igb/e1000_82575.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
case E1000_DEV_ID_82580_SERDES:
case E1000_DEV_ID_82580_SGMII:
case E1000_DEV_ID_82580_COPPER_DUAL:
case E1000_DEV_ID_DH89XXCC_SGMII:
case E1000_DEV_ID_DH89XXCC_SERDES:
mac->type = e1000_82580;
break;
case E1000_DEV_ID_I350_COPPER:
Expand Down Expand Up @@ -282,10 +284,18 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)

/* Verify phy id and set remaining function pointers */
switch (phy->id) {
case I347AT4_E_PHY_ID:
case M88E1112_E_PHY_ID:
case M88E1111_I_PHY_ID:
phy->type = e1000_phy_m88;
phy->ops.get_phy_info = igb_get_phy_info_m88;
phy->ops.get_cable_length = igb_get_cable_length_m88;

if (phy->id == I347AT4_E_PHY_ID ||
phy->id == M88E1112_E_PHY_ID)
phy->ops.get_cable_length = igb_get_cable_length_m88_gen2;
else
phy->ops.get_cable_length = igb_get_cable_length_m88;

phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88;
break;
case IGP03E1000_E_PHY_ID:
Expand Down Expand Up @@ -1058,7 +1068,11 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
}
switch (hw->phy.type) {
case e1000_phy_m88:
ret_val = igb_copper_link_setup_m88(hw);
if (hw->phy.id == I347AT4_E_PHY_ID ||
hw->phy.id == M88E1112_E_PHY_ID)
ret_val = igb_copper_link_setup_m88_gen2(hw);
else
ret_val = igb_copper_link_setup_m88(hw);
break;
case e1000_phy_igp_3:
ret_val = igb_copper_link_setup_igp(hw);
Expand Down
31 changes: 31 additions & 0 deletions drivers/net/igb/e1000_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,8 @@
* E = External
*/
#define M88E1111_I_PHY_ID 0x01410CC0
#define M88E1112_E_PHY_ID 0x01410C90
#define I347AT4_E_PHY_ID 0x01410DC0
#define IGP03E1000_E_PHY_ID 0x02A80390
#define I82580_I_PHY_ID 0x015403A0
#define I350_I_PHY_ID 0x015403B0
Expand Down Expand Up @@ -702,6 +704,35 @@
#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100
#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */

/* Intel i347-AT4 Registers */

#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */
#define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */
#define I347AT4_PAGE_SELECT 0x16

/* i347-AT4 Extended PHY Specific Control Register */

/*
* Number of times we will attempt to autonegotiate before downshifting if we
* are the master
*/
#define I347AT4_PSCR_DOWNSHIFT_ENABLE 0x0800
#define I347AT4_PSCR_DOWNSHIFT_MASK 0x7000
#define I347AT4_PSCR_DOWNSHIFT_1X 0x0000
#define I347AT4_PSCR_DOWNSHIFT_2X 0x1000
#define I347AT4_PSCR_DOWNSHIFT_3X 0x2000
#define I347AT4_PSCR_DOWNSHIFT_4X 0x3000
#define I347AT4_PSCR_DOWNSHIFT_5X 0x4000
#define I347AT4_PSCR_DOWNSHIFT_6X 0x5000
#define I347AT4_PSCR_DOWNSHIFT_7X 0x6000
#define I347AT4_PSCR_DOWNSHIFT_8X 0x7000

/* i347-AT4 PHY Cable Diagnostics Control */
#define I347AT4_PCDC_CABLE_LENGTH_UNIT 0x0400 /* 0=cm 1=meters */

/* Marvell 1112 only registers */
#define M88E1112_VCT_DSP_DISTANCE 0x001A

/* M88EC018 Rev 2 specific DownShift settings */
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/igb/e1000_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ struct e1000_hw;
#define E1000_DEV_ID_82580_SERDES 0x1510
#define E1000_DEV_ID_82580_SGMII 0x1511
#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516
#define E1000_DEV_ID_DH89XXCC_SGMII 0x0436
#define E1000_DEV_ID_DH89XXCC_SERDES 0x0438
#define E1000_DEV_ID_I350_COPPER 0x1521
#define E1000_DEV_ID_I350_FIBER 0x1522
#define E1000_DEV_ID_I350_SERDES 0x1523
Expand Down
206 changes: 194 additions & 12 deletions drivers/net/igb/e1000_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,89 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
return ret_val;
}

/**
* igb_copper_link_setup_m88_gen2 - Setup m88 PHY's for copper link
* @hw: pointer to the HW structure
*
* Sets up MDI/MDI-X and polarity for i347-AT4, m88e1322 and m88e1112 PHY's.
* Also enables and sets the downshift parameters.
**/
s32 igb_copper_link_setup_m88_gen2(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;
}

/* Enable CRS on Tx. This must be set for half-duplex operation. */
ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
goto out;

/*
* Options:
* MDI/MDI-X = 0 (default)
* 0 - Auto for all speeds
* 1 - MDI mode
* 2 - MDI-X mode
* 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
*/
phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;

switch (phy->mdix) {
case 1:
phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
break;
case 2:
phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
break;
case 3:
/* M88E1112 does not support this mode) */
if (phy->id != M88E1112_E_PHY_ID) {
phy_data |= M88E1000_PSCR_AUTO_X_1000T;
break;
}
case 0:
default:
phy_data |= M88E1000_PSCR_AUTO_X_MODE;
break;
}

/*
* Options:
* disable_polarity_correction = 0 (default)
* Automatic Correction for Reversed Cable Polarity
* 0 - Disabled
* 1 - Enabled
*/
phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
if (phy->disable_polarity_correction == 1)
phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;

/* Enable downshift and setting it to X6 */
phy_data &= ~I347AT4_PSCR_DOWNSHIFT_MASK;
phy_data |= I347AT4_PSCR_DOWNSHIFT_6X;
phy_data |= I347AT4_PSCR_DOWNSHIFT_ENABLE;

ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
if (ret_val)
goto out;

/* Commit the changes. */
ret_val = igb_phy_sw_reset(hw);
if (ret_val) {
hw_dbg("Error committing the PHY changes\n");
goto out;
}

out:
return ret_val;
}

/**
* igb_copper_link_setup_igp - Setup igp PHY's for copper link
* @hw: pointer to the HW structure
Expand Down Expand Up @@ -1124,18 +1207,25 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out;

if (!link) {
/*
* We didn't get link.
* Reset the DSP and cross our fingers.
*/
ret_val = phy->ops.write_reg(hw,
M88E1000_PHY_PAGE_SELECT,
0x001d);
if (ret_val)
goto out;
ret_val = igb_phy_reset_dsp(hw);
if (ret_val)
goto out;
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 {

/*
* We didn't get link.
* Reset the DSP and cross our fingers.
*/
ret_val = phy->ops.write_reg(hw,
M88E1000_PHY_PAGE_SELECT,
0x001d);
if (ret_val)
goto out;
ret_val = igb_phy_reset_dsp(hw);
if (ret_val)
goto out;
}
}

/* Try once more */
Expand All @@ -1145,6 +1235,11 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out;
}

if (hw->phy.type != e1000_phy_m88 ||
hw->phy.id == I347AT4_E_PHY_ID ||
hw->phy.id == M88E1112_E_PHY_ID)
goto out;

ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
goto out;
Expand Down Expand Up @@ -1557,6 +1652,93 @@ s32 igb_get_cable_length_m88(struct e1000_hw *hw)
return ret_val;
}

s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data, phy_data2, index, default_page, is_cm;

switch (hw->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,
&default_page);
if (ret_val)
goto out;

ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07);
if (ret_val)
goto out;

/* Get cable length from PHY Cable Diagnostics Control Reg */
ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr),
&phy_data);
if (ret_val)
goto out;

/* Check if the unit of cable length is meters or cm */
ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2);
if (ret_val)
goto out;

is_cm = !(phy_data & I347AT4_PCDC_CABLE_LENGTH_UNIT);

/* Populate the phy structure with cable length in meters */
phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
phy->cable_length = phy_data / (is_cm ? 100 : 1);

/* Reset the page selec to its original value */
ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT,
default_page);
if (ret_val)
goto out;
break;
case M88E1112_E_PHY_ID:
/* Remember the original page select and set it to 5 */
ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
&default_page);
if (ret_val)
goto out;

ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x05);
if (ret_val)
goto out;

ret_val = phy->ops.read_reg(hw, M88E1112_VCT_DSP_DISTANCE,
&phy_data);
if (ret_val)
goto out;

index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
M88E1000_PSSR_CABLE_LENGTH_SHIFT;
if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
ret_val = -E1000_ERR_PHY;
goto out;
}

phy->min_cable_length = e1000_m88_cable_length_table[index];
phy->max_cable_length = e1000_m88_cable_length_table[index + 1];

phy->cable_length = (phy->min_cable_length +
phy->max_cable_length) / 2;

/* Reset the page select to its original value */
ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT,
default_page);
if (ret_val)
goto out;

break;
default:
ret_val = -E1000_ERR_PHY;
goto out;
}

out:
return ret_val;
}

/**
* igb_get_cable_length_igp_2 - Determine cable length for igp2 PHY
* @hw: pointer to the HW structure
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/igb/e1000_phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ s32 igb_check_downshift(struct e1000_hw *hw);
s32 igb_check_reset_block(struct e1000_hw *hw);
s32 igb_copper_link_setup_igp(struct e1000_hw *hw);
s32 igb_copper_link_setup_m88(struct e1000_hw *hw);
s32 igb_copper_link_setup_m88_gen2(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw);
s32 igb_get_cable_length_m88(struct e1000_hw *hw);
s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw);
s32 igb_get_cable_length_igp_2(struct e1000_hw *hw);
s32 igb_get_phy_id(struct e1000_hw *hw);
s32 igb_get_phy_info_igp(struct e1000_hw *hw);
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/igb/igb_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SGMII), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 },
Expand Down

0 comments on commit 308fb39

Please sign in to comment.