igb: Add support for DH89xxCC
[pandora-kernel.git] / drivers / net / igb / e1000_phy.c
index cf1f323..ddd036a 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