Commit 308fb39a authored by Joseph Gasparakis's avatar Joseph Gasparakis Committed by David S. Miller

igb: Add support for DH89xxCC

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: default avatarJoseph Gasparakis <joseph.gasparakis@intel.com>
Tested-by: default avatarJeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d85b9004
...@@ -132,6 +132,8 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) ...@@ -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_SERDES:
case E1000_DEV_ID_82580_SGMII: case E1000_DEV_ID_82580_SGMII:
case E1000_DEV_ID_82580_COPPER_DUAL: case E1000_DEV_ID_82580_COPPER_DUAL:
case E1000_DEV_ID_DH89XXCC_SGMII:
case E1000_DEV_ID_DH89XXCC_SERDES:
mac->type = e1000_82580; mac->type = e1000_82580;
break; break;
case E1000_DEV_ID_I350_COPPER: case E1000_DEV_ID_I350_COPPER:
...@@ -282,10 +284,18 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) ...@@ -282,10 +284,18 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
/* Verify phy id and set remaining function pointers */ /* Verify phy id and set remaining function pointers */
switch (phy->id) { switch (phy->id) {
case I347AT4_E_PHY_ID:
case M88E1112_E_PHY_ID:
case M88E1111_I_PHY_ID: case M88E1111_I_PHY_ID:
phy->type = e1000_phy_m88; phy->type = e1000_phy_m88;
phy->ops.get_phy_info = igb_get_phy_info_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; phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88;
break; break;
case IGP03E1000_E_PHY_ID: case IGP03E1000_E_PHY_ID:
...@@ -1058,7 +1068,11 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) ...@@ -1058,7 +1068,11 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
} }
switch (hw->phy.type) { switch (hw->phy.type) {
case e1000_phy_m88: 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; break;
case e1000_phy_igp_3: case e1000_phy_igp_3:
ret_val = igb_copper_link_setup_igp(hw); ret_val = igb_copper_link_setup_igp(hw);
......
...@@ -634,6 +634,8 @@ ...@@ -634,6 +634,8 @@
* E = External * E = External
*/ */
#define M88E1111_I_PHY_ID 0x01410CC0 #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 IGP03E1000_E_PHY_ID 0x02A80390
#define I82580_I_PHY_ID 0x015403A0 #define I82580_I_PHY_ID 0x015403A0
#define I350_I_PHY_ID 0x015403B0 #define I350_I_PHY_ID 0x015403B0
...@@ -702,6 +704,35 @@ ...@@ -702,6 +704,35 @@
#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100
#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ #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 */ /* M88EC018 Rev 2 specific DownShift settings */
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 #define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 #define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800
......
...@@ -54,6 +54,8 @@ struct e1000_hw; ...@@ -54,6 +54,8 @@ struct e1000_hw;
#define E1000_DEV_ID_82580_SERDES 0x1510 #define E1000_DEV_ID_82580_SERDES 0x1510
#define E1000_DEV_ID_82580_SGMII 0x1511 #define E1000_DEV_ID_82580_SGMII 0x1511
#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516 #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_COPPER 0x1521
#define E1000_DEV_ID_I350_FIBER 0x1522 #define E1000_DEV_ID_I350_FIBER 0x1522
#define E1000_DEV_ID_I350_SERDES 0x1523 #define E1000_DEV_ID_I350_SERDES 0x1523
......
...@@ -569,6 +569,89 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw) ...@@ -569,6 +569,89 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
return ret_val; 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 * igb_copper_link_setup_igp - Setup igp PHY's for copper link
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
...@@ -1124,18 +1207,25 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw) ...@@ -1124,18 +1207,25 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out; goto out;
if (!link) { if (!link) {
/* if (hw->phy.type != e1000_phy_m88 ||
* We didn't get link. hw->phy.id == I347AT4_E_PHY_ID ||
* Reset the DSP and cross our fingers. hw->phy.id == M88E1112_E_PHY_ID) {
*/ hw_dbg("Link taking longer than expected.\n");
ret_val = phy->ops.write_reg(hw, } else {
M88E1000_PHY_PAGE_SELECT,
0x001d); /*
if (ret_val) * We didn't get link.
goto out; * Reset the DSP and cross our fingers.
ret_val = igb_phy_reset_dsp(hw); */
if (ret_val) ret_val = phy->ops.write_reg(hw,
goto out; 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 */ /* Try once more */
...@@ -1145,6 +1235,11 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw) ...@@ -1145,6 +1235,11 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out; 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); ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
if (ret_val) if (ret_val)
goto out; goto out;
...@@ -1557,6 +1652,93 @@ s32 igb_get_cable_length_m88(struct e1000_hw *hw) ...@@ -1557,6 +1652,93 @@ s32 igb_get_cable_length_m88(struct e1000_hw *hw)
return ret_val; 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 * igb_get_cable_length_igp_2 - Determine cable length for igp2 PHY
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
......
...@@ -45,9 +45,11 @@ s32 igb_check_downshift(struct e1000_hw *hw); ...@@ -45,9 +45,11 @@ s32 igb_check_downshift(struct e1000_hw *hw);
s32 igb_check_reset_block(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_igp(struct e1000_hw *hw);
s32 igb_copper_link_setup_m88(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_igp(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_m88(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(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_cable_length_igp_2(struct e1000_hw *hw);
s32 igb_get_phy_id(struct e1000_hw *hw); s32 igb_get_phy_id(struct e1000_hw *hw);
s32 igb_get_phy_info_igp(struct e1000_hw *hw); s32 igb_get_phy_info_igp(struct e1000_hw *hw);
......
...@@ -71,6 +71,8 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = { ...@@ -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_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), 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_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), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 },
......
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