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
