Hi,

we have the problem that on some HP laptops with i219V, it sometimes 
happens that em fails to attach with this error:

em0: Hardware Initialization Failed
em0: Unable to initialize the hardware

It seems there was some previous discussion of this issue here: 
http://bugs.openbsd.narkive.com/95VRrEJX/hardware-initialization-failed-with-intel-i219-v

The most reliable way to reproduce it is by booting Windows 10 first and 
then rebooting into openbsd without switching the laptop off. But 
sometimes, it also happens without having booted Windows first. Also, we 
had a case where the broken state persisted even if the laptop was 
switched off for a short time.  After the laptop had been switched off for 
several minutes, it worked again.

We have never seen the problem with i219LM (both on Fujitsu and HP 
laptops).

The attached patch seems to fix the problem. It ports the 
e1000_disable_ulp_lpt_lp() logic from the FreeBSD driver to disable 
ultra-low-power (ULP) mode.

The code has been merged in a way to make the diff from FreeBSD minimal.  
For example, the SWFW register is called H2ME on newer chips, so a new 
define is introduced. Also, the em_toggle_lanphypc_pch_lpt() function is 
left as separate function even though only used in one place at the 
moment.


For consumer NICs, the patch only does something on I218_LM_3/I218_V_3 or 
I219* or newer.  I am not quite sure which server chips are matching 
em_pch_spt. Testers, comments, OKs are welcome.

Cheers,
Stefan

diff --git a/sys/dev/pci/if_em_hw.c b/sys/dev/pci/if_em_hw.c
index d108555..8b3a0a0 100644
--- a/sys/dev/pci/if_em_hw.c
+++ b/sys/dev/pci/if_em_hw.c
@@ -93,6 +93,8 @@ static int32_t        em_init_lcd_from_nvm(struct em_hw *);
 static int32_t em_phy_no_cable_workaround(struct em_hw *);
 static void    em_init_rx_addrs(struct em_hw *);
 static void    em_initialize_hardware_bits(struct em_hw *);
+static void    em_toggle_lanphypc_pch_lpt(struct em_hw *);
+static int     em_disable_ulp_lpt_lp(struct em_hw *hw, bool force);
 static boolean_t em_is_onboard_nvm_eeprom(struct em_hw *);
 static int32_t em_kumeran_lock_loss_workaround(struct em_hw *);
 static int32_t em_mng_enable_host_if(struct em_hw *);
@@ -1194,6 +1196,192 @@ em_initialize_hardware_bits(struct em_hw *hw)
        }
 }
 
+/**
+ *  e1000_toggle_lanphypc_pch_lpt - toggle the LANPHYPC pin value
+ *  @hw: pointer to the HW structure
+ *
+ *  Toggling the LANPHYPC pin value fully power-cycles the PHY and is
+ *  used to reset the PHY to a quiescent state when necessary.
+ **/
+static void
+em_toggle_lanphypc_pch_lpt(struct em_hw *hw)
+{
+       uint32_t mac_reg;
+
+       DEBUGFUNC("e1000_toggle_lanphypc_pch_lpt");
+
+       /* Set Phy Config Counter to 50msec */
+       mac_reg = E1000_READ_REG(hw, FEXTNVM3);
+       mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
+       mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
+       E1000_WRITE_REG(hw, FEXTNVM3, mac_reg);
+
+       /* Toggle LANPHYPC Value bit */
+       mac_reg = E1000_READ_REG(hw, CTRL);
+       mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE;
+       mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
+       E1000_WRITE_REG(hw, CTRL, mac_reg);
+       E1000_WRITE_FLUSH(hw);
+       msec_delay(1);
+       mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+       E1000_WRITE_REG(hw, CTRL, mac_reg);
+       E1000_WRITE_FLUSH(hw);
+
+       if (hw->mac_type < em_pch_lpt) {
+               msec_delay(50);
+       } else {
+               uint16_t count = 20;
+
+               do {
+                       msec_delay(5);
+               } while (!(E1000_READ_REG(hw, CTRL_EXT) &
+                   E1000_CTRL_EXT_LPCD) && count--);
+
+               msec_delay(30);
+       }
+}
+
+/**
+ *  em_disable_ulp_lpt_lp - unconfigure Ultra Low Power mode for LynxPoint-LP
+ *  @hw: pointer to the HW structure
+ *  @force: boolean indicating whether or not to force disabling ULP
+ *
+ *  Un-configure ULP mode when link is up, the system is transitioned from
+ *  Sx or the driver is unloaded.  If on a Manageability Engine (ME) enabled
+ *  system, poll for an indication from ME that ULP has been un-configured.
+ *  If not on an ME enabled system, un-configure the ULP mode by software.
+ *
+ *  During nominal operation, this function is called when link is acquired
+ *  to disable ULP mode (force=FALSE); otherwise, for example when unloading
+ *  the driver or during Sx->S0 transitions, this is called with force=TRUE
+ *  to forcibly disable ULP.
+ */
+static int
+em_disable_ulp_lpt_lp(struct em_hw *hw, bool force)
+{
+       int ret_val = E1000_SUCCESS;
+       uint32_t mac_reg;
+       uint16_t phy_reg;
+       int i = 0;
+
+       if ((hw->mac_type < em_pch_lpt) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_V) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_LM2) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_V2))
+               return 0;
+
+       if (E1000_READ_REG(hw, FWSM) & E1000_FWSM_FW_VALID) {
+               if (force) {
+                       /* Request ME un-configure ULP mode in the PHY */
+                       mac_reg = E1000_READ_REG(hw, H2ME);
+                       mac_reg &= ~E1000_H2ME_ULP;
+                       mac_reg |= E1000_H2ME_ENFORCE_SETTINGS;
+                       E1000_WRITE_REG(hw, H2ME, mac_reg);
+               }
+
+               /* Poll up to 300msec for ME to clear ULP_CFG_DONE. */
+               while (E1000_READ_REG(hw, FWSM) & E1000_FWSM_ULP_CFG_DONE) {
+                       if (i++ == 30) {
+                               ret_val = -E1000_ERR_PHY;
+                               goto out;
+                       }
+
+                       msec_delay(10);
+               }
+               DEBUGOUT1("ULP_CONFIG_DONE cleared after %dmsec\n", i * 10);
+
+               if (force) {
+                       mac_reg = E1000_READ_REG(hw, H2ME);
+                       mac_reg &= ~E1000_H2ME_ENFORCE_SETTINGS;
+                       E1000_WRITE_REG(hw, H2ME, mac_reg);
+               } else {
+                       /* Clear H2ME.ULP after ME ULP configuration */
+                       mac_reg = E1000_READ_REG(hw, H2ME);
+                       mac_reg &= ~E1000_H2ME_ULP;
+                       E1000_WRITE_REG(hw, H2ME, mac_reg);
+               }
+
+               goto out;
+       }
+
+       ret_val = em_get_software_flag(hw);
+       if (ret_val)
+               goto out;
+
+       if (force)
+               /* Toggle LANPHYPC Value bit */
+               em_toggle_lanphypc_pch_lpt(hw);
+
+       /* Unforce SMBus mode in PHY */
+       ret_val = em_read_phy_reg(hw, CV_SMB_CTRL, &phy_reg);
+       if (ret_val) {
+               /* The MAC might be in PCIe mode, so temporarily force to
+                * SMBus mode in order to access the PHY.
+                */
+               mac_reg = E1000_READ_REG(hw, CTRL_EXT);
+               mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+               E1000_WRITE_REG(hw, CTRL_EXT, mac_reg);
+
+               msec_delay(50);
+
+               ret_val = em_read_phy_reg(hw, CV_SMB_CTRL, &phy_reg);
+               if (ret_val)
+                       goto release;
+       }
+       phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
+       em_write_phy_reg(hw, CV_SMB_CTRL, phy_reg);
+
+       /* Unforce SMBus mode in MAC */
+       mac_reg = E1000_READ_REG(hw, CTRL_EXT);
+       mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+       E1000_WRITE_REG(hw, CTRL_EXT, mac_reg);
+
+       /* When ULP mode was previously entered, K1 was disabled by the
+        * hardware.  Re-Enable K1 in the PHY when exiting ULP.
+        */
+       ret_val = em_read_phy_reg(hw, HV_PM_CTRL, &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg |= HV_PM_CTRL_K1_ENABLE;
+       em_write_phy_reg(hw, HV_PM_CTRL, phy_reg);
+
+       /* Clear ULP enabled configuration */
+       ret_val = em_read_phy_reg(hw, I218_ULP_CONFIG1, &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg &= ~(I218_ULP_CONFIG1_IND |
+                    I218_ULP_CONFIG1_STICKY_ULP |
+                    I218_ULP_CONFIG1_RESET_TO_SMBUS |
+                    I218_ULP_CONFIG1_WOL_HOST |
+                    I218_ULP_CONFIG1_INBAND_EXIT |
+                    I218_ULP_CONFIG1_EN_ULP_LANPHYPC |
+                    I218_ULP_CONFIG1_DIS_CLR_STICKY_ON_PERST |
+                    I218_ULP_CONFIG1_DISABLE_SMB_PERST);
+       em_write_phy_reg(hw, I218_ULP_CONFIG1, phy_reg);
+
+       /* Commit ULP changes by starting auto ULP configuration */
+       phy_reg |= I218_ULP_CONFIG1_START;
+       em_write_phy_reg(hw, I218_ULP_CONFIG1, phy_reg);
+
+       /* Clear Disable SMBus Release on PERST# in MAC */
+       mac_reg = E1000_READ_REG(hw, FEXTNVM7);
+       mac_reg &= ~E1000_FEXTNVM7_DISABLE_SMB_PERST;
+       E1000_WRITE_REG(hw, FEXTNVM7, mac_reg);
+
+release:
+       em_release_software_flag(hw);
+       if (force) {
+               em_phy_reset(hw);
+               msec_delay(50);
+       }
+out:
+       if (ret_val)
+               DEBUGOUT1("Error in ULP disable flow: %d\n", ret_val);
+
+       return ret_val;
+}
+
 /******************************************************************************
  * Performs basic configuration of the adapter.
  *
@@ -1255,6 +1443,7 @@ em_init_hw(struct em_hw *hw)
                if (hw->mac_type == em_pch2lan)
                        em_gate_hw_phy_config_ich8lan(hw, TRUE);
 
+               em_disable_ulp_lpt_lp(hw, TRUE);
                /*
                 * Reset the PHY before any acccess to it.  Doing so,
                 * ensures that the PHY is in a known good state before
diff --git a/sys/dev/pci/if_em_hw.h b/sys/dev/pci/if_em_hw.h
index 655f5c2..2e1fd07 100644
--- a/sys/dev/pci/if_em_hw.h
+++ b/sys/dev/pci/if_em_hw.h
@@ -1004,6 +1004,7 @@ struct em_ffvt_entry {
 #define E1000_MDICNFG  0x00E04  /* MDI Config - RW */
 #define E1000_SCTL     0x00024  /* SerDes Control - RW */
 #define E1000_FEXTNVM  0x00028  /* Future Extended NVM register */
+#define E1000_FEXTNVM3 0x0003C  /* Future Extended NVM 3 - RW */
 #define E1000_FEXTNVM4 0x00024  /* Future Extended NVM 4 - RW */
 #define E1000_FEXTNVM6 0x00010  /* Future Extended NVM 6 - RW */
 #define E1000_FCAL     0x00028  /* Flow Control Address Low - RW */
@@ -1198,6 +1199,7 @@ struct em_ffvt_entry {
 #define E1000_GSCL_4    0x05B1C /* PCI-Ex Statistic Control #4 */
 #define E1000_FACTPS    0x05B30 /* Function Active and Power State to MNG */
 #define E1000_SWSM      0x05B50 /* SW Semaphore */
+#define E1000_H2ME      E1000_SWSM /* Host to ME */
 #define E1000_FWSM      0x05B54 /* FW Semaphore */
 #define E1000_FFLT_DBG  0x05F04 /* Debug Register */
 #define E1000_HICR      0x08F00 /* Host Interface Control */
@@ -1234,8 +1236,10 @@ struct em_ffvt_entry {
 #define E1000_82542_MDIC     E1000_MDIC
 #define E1000_82542_SCTL     E1000_SCTL
 #define E1000_82542_FEXTNVM  E1000_FEXTNVM
+#define E1000_82542_FEXTNVM3 E1000_FEXTNVM3
 #define E1000_82542_FEXTNVM4 E1000_FEXTNVM4
 #define E1000_82542_FEXTNVM6 E1000_FEXTNVM6
+#define E1000_82542_FEXTNVM7 E1000_FEXTNVM7
 #define E1000_82542_FCAL     E1000_FCAL
 #define E1000_82542_FCAH     E1000_FCAH
 #define E1000_82542_FCT      E1000_FCT
@@ -1420,6 +1424,7 @@ struct em_ffvt_entry {
 #define E1000_82542_GSCL_4      E1000_GSCL_4
 #define E1000_82542_FACTPS      E1000_FACTPS
 #define E1000_82542_SWSM        E1000_SWSM
+#define E1000_82542_H2ME        E1000_H2ME
 #define E1000_82542_FWSM        E1000_FWSM
 #define E1000_82542_FFLT_DBG    E1000_FFLT_DBG
 #define E1000_82542_IAC         E1000_IAC
@@ -1445,6 +1450,9 @@ struct em_ffvt_entry {
 #define E1000_82542_KUMCTRLSTA E1000_KUMCTRLSTA
 #define E1000_82542_SW_FW_SYNC E1000_SW_FW_SYNC
 
+#define E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK    0x0C000000
+#define E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC  0x08000000
+
 #define E1000_FEXTNVM4_BEACON_DURATION_MASK    0x7
 #define E1000_FEXTNVM4_BEACON_DURATION_8USEC   0x7
 #define E1000_FEXTNVM4_BEACON_DURATION_16USEC  0x3
@@ -1648,7 +1656,9 @@ struct em_hw {
 #define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through 
PHYRST_N pin */
 #define E1000_CTRL_LANPHYPC_OVERRIDE 0x00010000 /* SW control of LANPHYPC */
 #define E1000_CTRL_LANPHYPC_VALUE    0x00020000 /* SW value of LANPHYPC */
-#define E1000_CTRL_EXT_PHYPDEN 0x00100000
+#define E1000_CTRL_EXT_FORCE_SMBUS   0x00000800 /* Force SMBus mode */
+#define E1000_CTRL_EXT_PHYPDEN       0x00100000
+
 #define E1000_CTRL_SWDPIN0  0x00040000  /* SWDPIN 0 value */
 #define E1000_CTRL_SWDPIN1  0x00080000  /* SWDPIN 1 value */
 #define E1000_CTRL_SWDPIN2  0x00100000  /* SWDPIN 2 value */
@@ -1780,6 +1790,7 @@ struct em_hw {
 #define E1000_CTRL_EXT_GPI1_EN   0x00000002 /* Maps SDP5 to GPI1 */
 #define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN
 #define E1000_CTRL_EXT_GPI2_EN   0x00000004 /* Maps SDP6 to GPI2 */
+#define E1000_CTRL_EXT_LPCD      0x00000004 /* LCD Power Cycle Done */
 #define E1000_CTRL_EXT_GPI3_EN   0x00000008 /* Maps SDP7 to GPI3 */
 #define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */
 #define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */
@@ -2315,10 +2326,14 @@ struct em_hw {
 #define E1000_SWSM_SWESMBI      0x00000002 /* FW Semaphore bit */
 #define E1000_SWSM_WMNG         0x00000004 /* Wake MNG Clock */
 #define E1000_SWSM_DRV_LOAD     0x00000008 /* Driver Loaded Bit */
+/* Host to ME */
+#define E1000_H2ME_ULP              0x00000800 /* ULP Indication Bit */
+#define E1000_H2ME_ENFORCE_SETTINGS 0x00001000 /* Enforce Settings */
 
 /* FW Semaphore Register */
 #define E1000_FWSM_MODE_MASK    0x0000000E /* FW mode */
 #define E1000_FWSM_MODE_SHIFT            1
+#define E1000_FWSM_ULP_CFG_DONE 0x00000400 /* Low power cfg done */
 #define E1000_FWSM_FW_VALID     0x00008000 /* FW established a valid mode */
 
 #define E1000_FWSM_RSPCIPHY        0x00000040 /* Reset PHY on PCI reset */
@@ -3707,6 +3722,24 @@ union ich8_hws_flash_regacc {
 #define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK   0x0FFF0000
 #define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT          16
 
+/* SMBus Control Phy Register */
+#define CV_SMB_CTRL             PHY_REG(769, 23)
+#define CV_SMB_CTRL_FORCE_SMBUS 0x0001
+
+/* I218 Ultra Low Power Configuration 1 Register */
+#define I218_ULP_CONFIG1                PHY_REG(779, 16)
+#define I218_ULP_CONFIG1_START          0x0001 /* Start auto ULP config */
+#define I218_ULP_CONFIG1_IND            0x0004 /* Pwr up from ULP indication */
+#define I218_ULP_CONFIG1_STICKY_ULP     0x0010 /* Set sticky ULP mode */
+#define I218_ULP_CONFIG1_INBAND_EXIT    0x0020 /* Inband on ULP exit */
+#define I218_ULP_CONFIG1_WOL_HOST       0x0040 /* WoL Host on ULP exit */
+#define I218_ULP_CONFIG1_RESET_TO_SMBUS 0x0100 /* Reset to SMBus mode */
+/* enable ULP even if when phy powered down via lanphypc */
+#define I218_ULP_CONFIG1_EN_ULP_LANPHYPC        0x0400
+/* disable clear of sticky ULP on PERST */
+#define I218_ULP_CONFIG1_DIS_CLR_STICKY_ON_PERST        0x0800
+#define I218_ULP_CONFIG1_DISABLE_SMB_PERST      0x1000 /* Disable on PERST# */
+
 /* Hanksville definitions */
 #define HV_INTC_FC_PAGE_START   768
 
@@ -3738,6 +3771,11 @@ union ich8_hws_flash_regacc {
 #define HV_KMRN_MODE_CTRL      PHY_REG(769, 16)
 #define HV_KMRN_MDIO_SLOW      0x0400
 
+/* PHY Power Management Control */
+#define HV_PM_CTRL              PHY_REG(770, 17)
+#define HV_PM_CTRL_K1_CLK_REQ           0x200
+#define HV_PM_CTRL_K1_ENABLE            0x4000
+
 /* I217 definitions */
 #define I2_DFT_CTRL            PHY_REG(769, 20)
 #define I2_SMBUS_CTRL          PHY_REG(769, 23)
@@ -3747,6 +3785,7 @@ union ich8_hws_flash_regacc {
 /* FEXTNVM registers */
 #define E1000_FEXTNVM7                          0xe4UL
 #define E1000_FEXTNVM7_SIDE_CLK_UNGATE          0x04UL
+#define E1000_FEXTNVM7_DISABLE_SMB_PERST        0x00000020
 #define E1000_FEXTNVM9                          0x5bb4UL
 #define E1000_FEXTNVM9_IOSFSB_CLKGATE_DIS       0x0800UL
 #define E1000_FEXTNVM9_IOSFSB_CLKREQ_DIS        0x1000UL

Reply via email to