e1000/base: i210 PLL workaround
authorJijiang Liu <jijiang.liu@intel.com>
Wed, 18 Jun 2014 10:28:10 +0000 (12:28 +0200)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Wed, 18 Jun 2014 21:29:24 +0000 (23:29 +0200)
Signed-off-by: Jijiang Liu <jijiang.liu@intel.com>
Acked-by: Helin Zhang <helin.zhang@intel.com>
Tested-by: Waterman Cao <waterman.cao@intel.com>
[Thomas: split code drop]

lib/librte_pmd_e1000/e1000/e1000_82575.c
lib/librte_pmd_e1000/e1000/e1000_82575.h
lib/librte_pmd_e1000/e1000/e1000_defines.h
lib/librte_pmd_e1000/e1000/e1000_i210.c
lib/librte_pmd_e1000/e1000/e1000_i210.h

index d7af608..d901212 100644 (file)
@@ -55,7 +55,6 @@ STATIC s32  e1000_check_for_link_media_swap(struct e1000_hw *hw);
 STATIC s32  e1000_get_cfg_done_82575(struct e1000_hw *hw);
 STATIC s32  e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed,
                                         u16 *duplex);
-STATIC s32  e1000_init_hw_82575(struct e1000_hw *hw);
 STATIC s32  e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw);
 STATIC s32  e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
                                           u16 *data);
@@ -449,6 +448,9 @@ STATIC s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
        else
        mac->ops.reset_hw = e1000_reset_hw_82575;
        /* hw initialization */
+       if ((mac->type == e1000_i210) || (mac->type == e1000_i211))
+               mac->ops.init_hw = e1000_init_hw_i210;
+       else
        mac->ops.init_hw = e1000_init_hw_82575;
        /* link setup */
        mac->ops.setup_link = e1000_setup_link_generic;
@@ -1460,7 +1462,7 @@ STATIC s32 e1000_reset_hw_82575(struct e1000_hw *hw)
  *
  *  This inits the hardware readying it for operation.
  **/
-STATIC s32 e1000_init_hw_82575(struct e1000_hw *hw)
+s32 e1000_init_hw_82575(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
        s32 ret_val;
index 2450e6a..290fa2a 100644 (file)
@@ -479,6 +479,7 @@ void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable);
 void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf);
 void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable);
 s32 e1000_init_nvm_params_82575(struct e1000_hw *hw);
+s32  e1000_init_hw_82575(struct e1000_hw *hw);
 
 enum e1000_promisc_type {
        e1000_promisc_disabled = 0,   /* all promisc modes disabled */
index febf304..544105c 100644 (file)
@@ -76,6 +76,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #define E1000_CTRL_EXT_EE_RST  0x00002000 /* Reinitialize from EEPROM */
 /* 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_SPD_BYPS        0x00008000 /* Speed Select Bypass */
 #define E1000_CTRL_EXT_RO_DIS  0x00020000 /* Relaxed Ordering disable */
 #define E1000_CTRL_EXT_DMA_DYN_CLK_EN  0x00080000 /* DMA Dynamic Clk Gating */
index 883526b..6529feb 100644 (file)
@@ -914,3 +914,87 @@ s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data)
 
        return __e1000_access_xmdio_reg(hw, addr, dev_addr, &data, false);
 }
+
+/**
+ * e1000_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.
+ **/
+STATIC s32 e1000_pll_workaround_i210(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u32 wuc, mdicnfg, ctrl_ext, reg_val;
+       u16 nvm_word, phy_word, pci_word, tmp_nvm;
+       int i;
+
+       /* Get and set needed register values */
+       wuc = E1000_READ_REG(hw, E1000_WUC);
+       mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG);
+       reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO;
+       E1000_WRITE_REG(hw, E1000_MDICNFG, reg_val);
+
+       /* Get data from NVM, or set default */
+       ret_val = e1000_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD,
+                                           &nvm_word);
+       if (ret_val != E1000_SUCCESS)
+               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 */
+               hw->phy.ops.read_reg(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 = E1000_SUCCESS;
+                       break;
+               } else {
+                       ret_val = -E1000_ERR_PHY;
+               }
+               hw->phy.ops.reset(hw);
+               ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE);
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+
+               E1000_WRITE_REG(hw, E1000_WUC, 0);
+               reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16);
+               E1000_WRITE_REG(hw, E1000_EEARBC, reg_val);
+
+               e1000_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
+               pci_word |= E1000_PCI_PMCSR_D3;
+               e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
+               msec_delay(1);
+               pci_word &= ~E1000_PCI_PMCSR_D3;
+               e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word);
+               reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16);
+               E1000_WRITE_REG(hw, E1000_EEARBC, reg_val);
+
+               /* restore WUC register */
+               E1000_WRITE_REG(hw, E1000_WUC, wuc);
+       }
+       /* restore MDICNFG setting */
+       E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg);
+       return ret_val;
+}
+
+/**
+ *  e1000_init_hw_i210 - Init hw for I210/I211
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize hw for i210 hw family.
+ **/
+s32 e1000_init_hw_i210(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_init_hw_i210");
+       if ((hw->mac.type >= e1000_i210) &&
+           !(e1000_get_flash_presence_i210(hw))) {
+               ret_val = e1000_pll_workaround_i210(hw);
+               if (ret_val != E1000_SUCCESS)
+                       return ret_val;
+       }
+       ret_val = e1000_init_hw_82575(hw);
+       return ret_val;
+}
index 44de54b..1d79f3a 100644 (file)
@@ -50,6 +50,7 @@ s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
                         u16 *data);
 s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
                          u16 data);
+s32 e1000_init_hw_i210(struct e1000_hw *hw);
 
 #define E1000_STM_OPCODE               0xDB00
 #define E1000_EEPROM_FLASH_SIZE_WORD   0x11
@@ -94,4 +95,16 @@ enum E1000_INVM_STRUCTURE_TYPE {
 #define NVM_INIT_CTRL_4_DEFAULT_I211   0x00C1
 #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