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

Commit 94826487 authored by Todd Fujinaka's avatar Todd Fujinaka Committed by David S. Miller
Browse files

igb: Workaround for i210 Errata 25: Slow System Clock



On some devices, the internal PLL circuit occasionally provides the
wrong clock frequency after power up. The probability of failure is less
than one failure per 1000 power cycles. When the failure occurs, the
internal clock frequency is around 1/20 of the correct frequency.

Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarTodd Fujinaka <todd.fujinaka@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b4df480f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1481,6 +1481,13 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
	s32 ret_val;
	u16 i, rar_count = mac->rar_entry_count;

	if ((hw->mac.type >= e1000_i210) &&
	    !(igb_get_flash_presence_i210(hw))) {
		ret_val = igb_pll_workaround_i210(hw);
		if (ret_val)
			return ret_val;
	}

	/* Initialize identification LED */
	ret_val = igb_id_led_init(hw);
	if (ret_val) {
+10 −8
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@

/* Physical Func Reset Done Indication */
#define E1000_CTRL_EXT_PFRSTD	0x00004000
#define E1000_CTRL_EXT_SDLPE	0X00040000  /* SerDes Low Power Enable */
#define E1000_CTRL_EXT_LINK_MODE_MASK	0x00C00000
#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES	0x00C00000
#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX	0x00400000
@@ -62,6 +63,7 @@
/* packet buffer parity error detection enabled */
/* descriptor FIFO parity error detection enable */
#define E1000_CTRL_EXT_PBA_CLR		0x80000000 /* PBA Clear */
#define E1000_CTRL_EXT_PHYPDEN		0x00100000
#define E1000_I2CCMD_REG_ADDR_SHIFT	16
#define E1000_I2CCMD_PHY_ADDR_SHIFT	24
#define E1000_I2CCMD_OPCODE_READ	0x08000000
+3 −0
Original line number Diff line number Diff line
@@ -567,4 +567,7 @@ struct net_device *igb_get_hw_dev(struct e1000_hw *hw);
/* These functions must be implemented by drivers */
s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);

void igb_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value);
void igb_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value);
#endif /* _E1000_HW_H_ */
+66 −0
Original line number Diff line number Diff line
@@ -834,3 +834,69 @@ s32 igb_init_nvm_params_i210(struct e1000_hw *hw)
	}
	return ret_val;
}

/**
 * igb_pll_workaround_i210
 * @hw: pointer to the HW structure
 *
 * Works around an errata in the PLL circuit where it occasionally
 * provides the wrong clock frequency after power up.
 **/
s32 igb_pll_workaround_i210(struct e1000_hw *hw)
{
	s32 ret_val;
	u32 wuc, mdicnfg, ctrl, ctrl_ext, reg_val;
	u16 nvm_word, phy_word, pci_word, tmp_nvm;
	int i;

	/* Get and set needed register values */
	wuc = rd32(E1000_WUC);
	mdicnfg = rd32(E1000_MDICNFG);
	reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO;
	wr32(E1000_MDICNFG, reg_val);

	/* Get data from NVM, or set default */
	ret_val = igb_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD,
					  &nvm_word);
	if (ret_val)
		nvm_word = E1000_INVM_DEFAULT_AL;
	tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL;
	for (i = 0; i < E1000_MAX_PLL_TRIES; i++) {
		/* check current state directly from internal PHY */
		igb_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE |
					 E1000_PHY_PLL_FREQ_REG), &phy_word);
		if ((phy_word & E1000_PHY_PLL_UNCONF)
		    != E1000_PHY_PLL_UNCONF) {
			ret_val = 0;
			break;
		} else {
			ret_val = -E1000_ERR_PHY;
		}
		/* directly reset the internal PHY */
		ctrl = rd32(E1000_CTRL);
		wr32(E1000_CTRL, ctrl|E1000_CTRL_PHY_RST);

		ctrl_ext = rd32(E1000_CTRL_EXT);
		ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE);
		wr32(E1000_CTRL_EXT, ctrl_ext);

		wr32(E1000_WUC, 0);
		reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16);
		wr32(E1000_EEARBC_I210, reg_val);

		igb_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
		pci_word |= E1000_PCI_PMCSR_D3;
		igb_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
		usleep_range(1000, 2000);
		pci_word &= ~E1000_PCI_PMCSR_D3;
		igb_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
		reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16);
		wr32(E1000_EEARBC_I210, reg_val);

		/* restore WUC register */
		wr32(E1000_WUC, wuc);
	}
	/* restore MDICNFG setting */
	wr32(E1000_MDICNFG, mdicnfg);
	return ret_val;
}
+12 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data);
s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data);
s32 igb_init_nvm_params_i210(struct e1000_hw *hw);
bool igb_get_flash_presence_i210(struct e1000_hw *hw);
s32 igb_pll_workaround_i210(struct e1000_hw *hw);

#define E1000_STM_OPCODE		0xDB00
#define E1000_EEPROM_FLASH_SIZE_WORD	0x11
@@ -78,4 +79,15 @@ enum E1000_INVM_STRUCTURE_TYPE {
#define NVM_LED_1_CFG_DEFAULT_I211	0x0184
#define NVM_LED_0_2_CFG_DEFAULT_I211	0x200C

/* PLL Defines */
#define E1000_PCI_PMCSR			0x44
#define E1000_PCI_PMCSR_D3		0x03
#define E1000_MAX_PLL_TRIES		5
#define E1000_PHY_PLL_UNCONF		0xFF
#define E1000_PHY_PLL_FREQ_PAGE		0xFC0000
#define E1000_PHY_PLL_FREQ_REG		0x000E
#define E1000_INVM_DEFAULT_AL		0x202F
#define E1000_INVM_AUTOLOAD		0x0A
#define E1000_INVM_PLL_WO_VAL		0x0010

#endif
Loading