|
@@ -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
|