igb: Add support for DH89xxCC
authorJoseph Gasparakis <joseph.gasparakis@intel.com>
Wed, 22 Sep 2010 17:56:44 +0000 (17:56 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 23 Sep 2010 04:20:04 +0000 (21:20 -0700)
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>
drivers/net/igb/e1000_82575.c
drivers/net/igb/e1000_defines.h
drivers/net/igb/e1000_hw.h
drivers/net/igb/e1000_phy.c
drivers/net/igb/e1000_phy.h
drivers/net/igb/igb_main.c

index 187622f1c81611f3b5b269c7c730610460c03210..bc183f5487cb11e668bea5e29b69ce2cf8fbded8 100644 (file)
@@ -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:
@@ -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:
@@ -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);
index bbd2ec308eb06b07963bfc5d658d73318968c406..62222796a8b37a771b28b4bedb544b5e542df0e8 100644 (file)
  * 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
 #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
index cb8db78b1a05ba4f241cab3c98c617213e1d3b16..c0b017f8d782e12c1021e61101b89b731e05378e 100644 (file)
@@ -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
index cf1f323009233aed82c601b0d73454bdfe7200e1..ddd036a78999c145ade02b705b2956b3260abc96 100644 (file)
@@ -569,6 +569,89 @@ out:
        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
@@ -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 */
@@ -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;
@@ -1557,6 +1652,93 @@ out:
        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
index 565a6dbb3714418d7776c8a4ceb2095f27e793db..2cc117705a316bf4c073095790fb4104f11374e6 100644 (file)
@@ -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);
index fd922e7db1d50aa25c724e569ed9e30c095b3e38..61892b80d7ada4eaff14c0e6d1987de87e24eab9 100644 (file)
@@ -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 },