Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -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;

		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,6 +1068,10 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
	}
	switch (hw->phy.type) {
	case e1000_phy_m88:
		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:
+31 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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
+194 −12
Original line number Diff line number Diff line
@@ -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
@@ -1124,6 +1207,12 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
			goto out;

		if (!link) {
			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.
@@ -1137,6 +1226,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
				if (ret_val)
					goto out;
			}
		}

		/* Try once more */
		ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT,
@@ -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 @@ 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
+2 −0
Original line number Diff line number Diff line
@@ -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);
Loading