Commit 1ae289b0 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2024-03-27 (e1000e)

This series contains updates to e1000e driver only.

Vitaly adds retry mechanism for some PHY operations to workaround MDI
error and moves SMBus configuration to avoid possible PHY loss.

* '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue:
  e1000e: move force SMBUS from enable ulp function to avoid PHY loss issue
  e1000e: Workaround for sporadic MDI error on Meteor Lake systems
====================

Link: https://lore.kernel.org/r/20240327185517.2587564-1-anthony.l.nguyen@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 03796540 861e8086
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -628,6 +628,7 @@ struct e1000_phy_info {
	u32 id;
	u32 reset_delay_us;	/* in usec */
	u32 revision;
	u32 retry_count;

	enum e1000_media_type media_type;

@@ -644,6 +645,7 @@ struct e1000_phy_info {
	bool polarity_correction;
	bool speed_downgraded;
	bool autoneg_wait_to_complete;
	bool retry_enabled;
};

struct e1000_nvm_info {
+26 −12
Original line number Diff line number Diff line
@@ -222,11 +222,18 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw)
	if (hw->mac.type >= e1000_pch_lpt) {
		/* Only unforce SMBus if ME is not active */
		if (!(er32(FWSM) & E1000_ICH_FWSM_FW_VALID)) {
			/* Switching PHY interface always returns MDI error
			 * so disable retry mechanism to avoid wasting time
			 */
			e1000e_disable_phy_retry(hw);

			/* Unforce SMBus mode in PHY */
			e1e_rphy_locked(hw, CV_SMB_CTRL, &phy_reg);
			phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
			e1e_wphy_locked(hw, CV_SMB_CTRL, phy_reg);

			e1000e_enable_phy_retry(hw);

			/* Unforce SMBus mode in MAC */
			mac_reg = er32(CTRL_EXT);
			mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
@@ -310,6 +317,11 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
		goto out;
	}

	/* There is no guarantee that the PHY is accessible at this time
	 * so disable retry mechanism to avoid wasting time
	 */
	e1000e_disable_phy_retry(hw);

	/* The MAC-PHY interconnect may be in SMBus mode.  If the PHY is
	 * inaccessible and resetting the PHY is not blocked, toggle the
	 * LANPHYPC Value bit to force the interconnect to PCIe mode.
@@ -380,6 +392,8 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
		break;
	}

	e1000e_enable_phy_retry(hw);

	hw->phy.ops.release(hw);
	if (!ret_val) {

@@ -449,6 +463,11 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)

	phy->id = e1000_phy_unknown;

	if (hw->mac.type == e1000_pch_mtp) {
		phy->retry_count = 2;
		e1000e_enable_phy_retry(hw);
	}

	ret_val = e1000_init_phy_workarounds_pchlan(hw);
	if (ret_val)
		return ret_val;
@@ -1146,18 +1165,6 @@ s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx)
	if (ret_val)
		goto out;

	/* Force SMBus mode in PHY */
	ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
	if (ret_val)
		goto release;
	phy_reg |= CV_SMB_CTRL_FORCE_SMBUS;
	e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);

	/* Force SMBus mode in MAC */
	mac_reg = er32(CTRL_EXT);
	mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
	ew32(CTRL_EXT, mac_reg);

	/* Si workaround for ULP entry flow on i127/rev6 h/w.  Enable
	 * LPLU and disable Gig speed when entering ULP
	 */
@@ -1313,6 +1320,11 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
		/* Toggle LANPHYPC Value bit */
		e1000_toggle_lanphypc_pch_lpt(hw);

	/* Switching PHY interface always returns MDI error
	 * so disable retry mechanism to avoid wasting time
	 */
	e1000e_disable_phy_retry(hw);

	/* Unforce SMBus mode in PHY */
	ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
	if (ret_val) {
@@ -1333,6 +1345,8 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
	phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
	e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);

	e1000e_enable_phy_retry(hw);

	/* Unforce SMBus mode in MAC */
	mac_reg = er32(CTRL_EXT);
	mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+18 −0
Original line number Diff line number Diff line
@@ -6623,6 +6623,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
	struct e1000_hw *hw = &adapter->hw;
	u32 ctrl, ctrl_ext, rctl, status, wufc;
	int retval = 0;
	u16 smb_ctrl;

	/* Runtime suspend should only enable wakeup for link changes */
	if (runtime)
@@ -6696,6 +6697,23 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
			if (retval)
				return retval;
		}

		/* Force SMBUS to allow WOL */
		/* Switching PHY interface always returns MDI error
		 * so disable retry mechanism to avoid wasting time
		 */
		e1000e_disable_phy_retry(hw);

		e1e_rphy(hw, CV_SMB_CTRL, &smb_ctrl);
		smb_ctrl |= CV_SMB_CTRL_FORCE_SMBUS;
		e1e_wphy(hw, CV_SMB_CTRL, smb_ctrl);

		e1000e_enable_phy_retry(hw);

		/* Force SMBus mode in MAC */
		ctrl_ext = er32(CTRL_EXT);
		ctrl_ext |= E1000_CTRL_EXT_FORCE_SMBUS;
		ew32(CTRL_EXT, ctrl_ext);
	}

	/* Ensure that the appropriate bits are set in LPI_CTRL
+113 −69
Original line number Diff line number Diff line
@@ -107,6 +107,16 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw)
	return e1e_wphy(hw, M88E1000_PHY_GEN_CONTROL, 0);
}

void e1000e_disable_phy_retry(struct e1000_hw *hw)
{
	hw->phy.retry_enabled = false;
}

void e1000e_enable_phy_retry(struct e1000_hw *hw)
{
	hw->phy.retry_enabled = true;
}

/**
 *  e1000e_read_phy_reg_mdic - Read MDI control register
 *  @hw: pointer to the HW structure
@@ -118,18 +128,24 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw)
 **/
s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
{
	u32 i, mdic = 0, retry_counter, retry_max;
	struct e1000_phy_info *phy = &hw->phy;
	u32 i, mdic = 0;
	bool success;

	if (offset > MAX_PHY_REG_ADDRESS) {
		e_dbg("PHY Address %d is out of range\n", offset);
		return -E1000_ERR_PARAM;
	}

	retry_max = phy->retry_enabled ? phy->retry_count : 0;

	/* Set up Op-code, Phy Address, and register offset in the MDI
	 * Control register.  The MAC will take care of interfacing with the
	 * PHY to retrieve the desired data.
	 */
	for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) {
		success = true;

		mdic = ((offset << E1000_MDIC_REG_SHIFT) |
			(phy->addr << E1000_MDIC_PHY_SHIFT) |
			(E1000_MDIC_OP_READ));
@@ -141,34 +157,46 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
		 * the lower time out
		 */
		for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
		udelay(50);
			usleep_range(50, 60);
			mdic = er32(MDIC);
			if (mdic & E1000_MDIC_READY)
				break;
		}
		if (!(mdic & E1000_MDIC_READY)) {
		e_dbg("MDI Read PHY Reg Address %d did not complete\n", offset);
		return -E1000_ERR_PHY;
			e_dbg("MDI Read PHY Reg Address %d did not complete\n",
			      offset);
			success = false;
		}
		if (mdic & E1000_MDIC_ERROR) {
			e_dbg("MDI Read PHY Reg Address %d Error\n", offset);
		return -E1000_ERR_PHY;
			success = false;
		}
		if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
			e_dbg("MDI Read offset error - requested %d, returned %d\n",
			      offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
		return -E1000_ERR_PHY;
			success = false;
		}
	*data = (u16)mdic;

		/* Allow some time after each MDIC transaction to avoid
		 * reading duplicate data in the next MDIC transaction.
		 */
		if (hw->mac.type == e1000_pch2lan)
		udelay(100);
			usleep_range(100, 150);

		if (success) {
			*data = (u16)mdic;
			return 0;
		}

		if (retry_counter != retry_max) {
			e_dbg("Perform retry on PHY transaction...\n");
			mdelay(10);
		}
	}

	return -E1000_ERR_PHY;
}

/**
 *  e1000e_write_phy_reg_mdic - Write MDI control register
 *  @hw: pointer to the HW structure
@@ -179,18 +207,24 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
 **/
s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
{
	u32 i, mdic = 0, retry_counter, retry_max;
	struct e1000_phy_info *phy = &hw->phy;
	u32 i, mdic = 0;
	bool success;

	if (offset > MAX_PHY_REG_ADDRESS) {
		e_dbg("PHY Address %d is out of range\n", offset);
		return -E1000_ERR_PARAM;
	}

	retry_max = phy->retry_enabled ? phy->retry_count : 0;

	/* Set up Op-code, Phy Address, and register offset in the MDI
	 * Control register.  The MAC will take care of interfacing with the
	 * PHY to retrieve the desired data.
	 */
	for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) {
		success = true;

		mdic = (((u32)data) |
			(offset << E1000_MDIC_REG_SHIFT) |
			(phy->addr << E1000_MDIC_PHY_SHIFT) |
@@ -203,32 +237,42 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
		 * the lower time out
		 */
		for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
		udelay(50);
			usleep_range(50, 60);
			mdic = er32(MDIC);
			if (mdic & E1000_MDIC_READY)
				break;
		}
		if (!(mdic & E1000_MDIC_READY)) {
		e_dbg("MDI Write PHY Reg Address %d did not complete\n", offset);
		return -E1000_ERR_PHY;
			e_dbg("MDI Write PHY Reg Address %d did not complete\n",
			      offset);
			success = false;
		}
		if (mdic & E1000_MDIC_ERROR) {
		e_dbg("MDI Write PHY Red Address %d Error\n", offset);
		return -E1000_ERR_PHY;
			e_dbg("MDI Write PHY Reg Address %d Error\n", offset);
			success = false;
		}
		if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
			e_dbg("MDI Write offset error - requested %d, returned %d\n",
			      offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
		return -E1000_ERR_PHY;
			success = false;
		}

		/* Allow some time after each MDIC transaction to avoid
		 * reading duplicate data in the next MDIC transaction.
		 */
		if (hw->mac.type == e1000_pch2lan)
		udelay(100);
			usleep_range(100, 150);

		if (success)
			return 0;

		if (retry_counter != retry_max) {
			e_dbg("Perform retry on PHY transaction...\n");
			mdelay(10);
		}
	}

	return -E1000_ERR_PHY;
}

/**
+2 −0
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data);
void e1000_power_up_phy_copper(struct e1000_hw *hw);
void e1000_power_down_phy_copper(struct e1000_hw *hw);
void e1000e_disable_phy_retry(struct e1000_hw *hw);
void e1000e_enable_phy_retry(struct e1000_hw *hw);
s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);