From 3645adbbab4329dd83a41674a56bf4afcdcc5914 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Sat, 13 Oct 2012 05:00:06 +0000 Subject: [PATCH 1/6] ixgbe: fix uninitialized event.type in ixgbe_ptp_check_pps_event This patch fixes a bug in ixgbe_ptp_check_pps_event where the type was uninitialized and could cause unknown event outcomes. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 842ba15550a5..01d99af0b9ba 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -387,6 +387,15 @@ void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr) struct ixgbe_hw *hw = &adapter->hw; struct ptp_clock_event event; + event.type = PTP_CLOCK_PPS; + + /* this check is necessary in case the interrupt was enabled via some + * alternative means (ex. debug_fs). Better to check here than + * everywhere that calls this function. + */ + if (!adapter->ptp_clock) + return; + switch (hw->mac.type) { case ixgbe_mac_X540: ptp_clock_event(adapter->ptp_clock, &event); From 3af3361e6c25ea58c72c31efa9f0eb9f9bee4749 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 24 Oct 2012 08:12:10 +0000 Subject: [PATCH 2/6] ixgbe: fix default setting of TXDCTL.WTHRESH The q_vector->itr check in ixgbe_configure_tx_ring() was done prior to it being set, which resulted in TXDCTL.WTHRESH always being set to 1 on driver load, while consequent resets would set it to 8. This patch moves the setting of q_vector->itr in ixgbe_alloc_q_vector() to make sure that TXDCTL.WTHRESH is set to 8 by default. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 15 ++++++++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 20 ------------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 5e508b614dc3..8c74f739011d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -824,6 +824,21 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, /* initialize pointer to rings */ ring = q_vector->ring; + /* intialize ITR */ + if (txr_count && !rxr_count) { + /* tx only vector */ + if (adapter->tx_itr_setting == 1) + q_vector->itr = IXGBE_10K_ITR; + else + q_vector->itr = adapter->tx_itr_setting; + } else { + /* rx or rx/tx vector */ + if (adapter->rx_itr_setting == 1) + q_vector->itr = IXGBE_20K_ITR; + else + q_vector->itr = adapter->rx_itr_setting; + } + while (txr_count) { /* assign generic ring traits */ ring->dev = &adapter->pdev->dev; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 26bfaaee9515..8b1a38bec237 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1976,20 +1976,6 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) ixgbe_for_each_ring(ring, q_vector->tx) ixgbe_set_ivar(adapter, 1, ring->reg_idx, v_idx); - if (q_vector->tx.ring && !q_vector->rx.ring) { - /* tx only vector */ - if (adapter->tx_itr_setting == 1) - q_vector->itr = IXGBE_10K_ITR; - else - q_vector->itr = adapter->tx_itr_setting; - } else { - /* rx or rx/tx vector */ - if (adapter->rx_itr_setting == 1) - q_vector->itr = IXGBE_20K_ITR; - else - q_vector->itr = adapter->rx_itr_setting; - } - ixgbe_write_eitr(q_vector); } @@ -2761,12 +2747,6 @@ static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter) { struct ixgbe_q_vector *q_vector = adapter->q_vector[0]; - /* rx/tx vector */ - if (adapter->rx_itr_setting == 1) - q_vector->itr = IXGBE_20K_ITR; - else - q_vector->itr = adapter->rx_itr_setting; - ixgbe_write_eitr(q_vector); ixgbe_set_ivar(adapter, 0, 0, 0); From 40b20122f92b9e29d7770c12eb58b3a30e1cce33 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Fri, 19 Oct 2012 05:31:43 +0000 Subject: [PATCH 3/6] igb: Remove workaround for EEE configuration on i210/I211 This patch removes a workaround that was needed on pre-release hardware. Released hardware should not have this setting, but any devices that do will get a warning message instead. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index ca4641e2f748..4313bcca876e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -2233,19 +2233,16 @@ s32 igb_set_eee_i350(struct e1000_hw *hw) /* enable or disable per user setting */ if (!(hw->dev_spec._82575.eee_disable)) { - ipcnfg |= (E1000_IPCNFG_EEE_1G_AN | - E1000_IPCNFG_EEE_100M_AN); - eeer |= (E1000_EEER_TX_LPI_EN | - E1000_EEER_RX_LPI_EN | + u32 eee_su = rd32(E1000_EEE_SU); + + ipcnfg |= (E1000_IPCNFG_EEE_1G_AN | E1000_IPCNFG_EEE_100M_AN); + eeer |= (E1000_EEER_TX_LPI_EN | E1000_EEER_RX_LPI_EN | E1000_EEER_LPI_FC); - /* keep the LPI clock running before EEE is enabled */ - if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) { - u32 eee_su; - eee_su = rd32(E1000_EEE_SU); - eee_su &= ~E1000_EEE_SU_LPI_CLK_STP; - wr32(E1000_EEE_SU, eee_su); - } + /* This bit should not be set in normal operation. */ + if (eee_su & E1000_EEE_SU_LPI_CLK_STP) + hw_dbg("LPI Clock Stop Bit should not be set!\n"); + } else { ipcnfg &= ~(E1000_IPCNFG_EEE_1G_AN | From 09e77287e752c8fc9743d865ddadc1a0d81a4927 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Tue, 23 Oct 2012 13:04:37 +0000 Subject: [PATCH 4/6] igb: Add function to read i211's invm version The i211's one-time programmable (invm) version field is different than the other fields contained in it. This patch adds a function to get the invm version of it and store it for output from ethtool. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_i210.c | 94 +++++++++++++++++++++ drivers/net/ethernet/intel/igb/e1000_i210.h | 11 +++ drivers/net/ethernet/intel/igb/e1000_mac.h | 1 + drivers/net/ethernet/intel/igb/e1000_nvm.c | 1 + 4 files changed, 107 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c index 77a5f939bc74..41474298d365 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.c +++ b/drivers/net/ethernet/intel/igb/e1000_i210.c @@ -422,6 +422,100 @@ s32 igb_read_invm_i211(struct e1000_hw *hw, u16 address, u16 *data) return status; } +/** + * igb_read_invm_version - Reads iNVM version and image type + * @hw: pointer to the HW structure + * @invm_ver: version structure for the version read + * + * Reads iNVM version and image type. + **/ +s32 igb_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver) { + u32 *record = NULL; + u32 *next_record = NULL; + u32 i = 0; + u32 invm_dword = 0; + u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE / + E1000_INVM_RECORD_SIZE_IN_BYTES); + u32 buffer[E1000_INVM_SIZE]; + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u16 version = 0; + + /* Read iNVM memory */ + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = rd32(E1000_INVM_DATA_REG(i)); + buffer[i] = invm_dword; + } + + /* Read version number */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have first version location used */ + if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) { + version = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have second version location used */ + else if ((i == 1) && + ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + /* Check if we have odd version location + * used and it is the last one used + */ + else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) && + ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) && + (i != 1))) { + version = (*next_record & E1000_INVM_VER_FIELD_TWO) + >> 13; + status = E1000_SUCCESS; + break; + } + /* Check if we have even version location + * used and it is the last one used + */ + else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) && + ((*record & 0x3) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + } + + if (status == E1000_SUCCESS) { + invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK) + >> E1000_INVM_MAJOR_SHIFT; + invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK; + } + /* Read Image Type */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have image type in first location used */ + if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) { + invm_ver->invm_img_type = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have image type in first location used */ + else if ((((*record & 0x3) == 0) && + ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) || + ((((*record & 0x3) != 0) && (i != 1)))) { + invm_ver->invm_img_type = + (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23; + status = E1000_SUCCESS; + break; + } + } + return status; +} + /** * igb_validate_nvm_checksum_i210 - Validate EEPROM checksum * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index 5dc2bd3f50bc..974d23584d70 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -43,6 +43,8 @@ extern void igb_release_nvm_i210(struct e1000_hw *hw); extern s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data); extern s32 igb_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +extern s32 igb_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver); #define E1000_STM_OPCODE 0xDB00 #define E1000_EEPROM_FLASH_SIZE_WORD 0x11 @@ -65,6 +67,15 @@ enum E1000_INVM_STRUCTURE_TYPE { #define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS 8 #define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS 1 +#define E1000_INVM_ULT_BYTES_SIZE 8 +#define E1000_INVM_RECORD_SIZE_IN_BYTES 4 +#define E1000_INVM_VER_FIELD_ONE 0x1FF8 +#define E1000_INVM_VER_FIELD_TWO 0x7FE000 +#define E1000_INVM_IMGTYPE_FIELD 0x1F800000 + +#define E1000_INVM_MAJOR_MASK 0x3F0 +#define E1000_INVM_MINOR_MASK 0xF +#define E1000_INVM_MAJOR_SHIFT 4 #define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ (ID_LED_OFF1_OFF2 << 4) | \ diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h index cbddc4e51e30..e2b2c4b9c951 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.h +++ b/drivers/net/ethernet/intel/igb/e1000_mac.h @@ -33,6 +33,7 @@ #include "e1000_phy.h" #include "e1000_nvm.h" #include "e1000_defines.h" +#include "e1000_i210.h" /* * Functions that should not be called directly from drivers but can be used diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index 54ff53905ffe..7db3f80bcd57 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -727,6 +727,7 @@ void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers) switch (hw->mac.type) { case e1000_i211: + igb_read_invm_version(hw, fw_vers); return; case e1000_82575: case e1000_82576: From ede4126efc93dcee555100c8fe00ed201713e800 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 24 Oct 2012 03:56:21 +0000 Subject: [PATCH 5/6] igb: Fix EEPROM writes via ethtool on i210 This patch fixes a problem where the driver would crash when trying to write a word to the EEPROM on i210 devices. Reported-by: Ekman Tsang Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 4313bcca876e..deb05970b9f1 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -319,6 +319,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) nvm->ops.acquire = igb_acquire_nvm_i210; nvm->ops.release = igb_release_nvm_i210; nvm->ops.read = igb_read_nvm_srrd_i210; + nvm->ops.write = igb_write_nvm_srwr_i210; nvm->ops.valid_led_default = igb_valid_led_default_i210; break; case e1000_i211: From ac56558fc6ed4943ba4d9dd0046437d4fb05e8b6 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Sat, 20 Oct 2012 14:53:40 +0000 Subject: [PATCH 6/6] e1000: fix concurrent accesses to PHY from watchdog and ethtool The e1000 driver currently does not protect concurrent accesses to the PHY from both the ethtool callbacks, and from the e1000_watchdog function. This patchs adds a new spinlock which is used by e1000_{read,write}_phy_reg in order to serialize concurrent accesses to the PHY. Signed-off-by: Maxime Bizon Signed-off-by: Florian Fainelli Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 3d6839528761..8fedd2451538 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -107,6 +107,7 @@ u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = { }; static DEFINE_SPINLOCK(e1000_eeprom_lock); +static DEFINE_SPINLOCK(e1000_phy_lock); /** * e1000_set_phy_type - Set the phy type member in the hw struct. @@ -2830,19 +2831,25 @@ static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data) { u32 ret_val; + unsigned long flags; e_dbg("e1000_read_phy_reg"); + spin_lock_irqsave(&e1000_phy_lock, flags); + if ((hw->phy_type == e1000_phy_igp) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, (u16) reg_addr); - if (ret_val) + if (ret_val) { + spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; + } } ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, phy_data); + spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; } @@ -2965,19 +2972,25 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data) { u32 ret_val; + unsigned long flags; e_dbg("e1000_write_phy_reg"); + spin_lock_irqsave(&e1000_phy_lock, flags); + if ((hw->phy_type == e1000_phy_igp) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, (u16) reg_addr); - if (ret_val) + if (ret_val) { + spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; + } } ret_val = e1000_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, phy_data); + spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; }