Commit 1316d595 authored by Sara Sharon's avatar Sara Sharon Committed by Luca Coelho

iwlwifi: pcie: workaround HW shadow registers bug

Integrated 9000 devices have a bug with shadow registers
value retention.
If driver writes RBD registers while MAC is asleep the
values are stored in shadow registers to be copied whenever
MAC wakes up.
However, in 9000 devices a MAC wakeup is not triggered
and when the bus powers down due to inactivity the shadow
values and dirty bits are lost.
Turn on the chicken-bits that cause MAC wakeup for RX-related
values as well when the device is in D0.
When the device is in low power mode turn the RX wakeup chicken
bits off since driver is idle and this W/A is not needed.
Remove previous W/A which was ineffective.
Signed-off-by: default avatarSara Sharon <sara.sharon@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent a6f035a0
...@@ -178,6 +178,7 @@ const struct iwl_cfg iwl5165_2ac_cfg = { ...@@ -178,6 +178,7 @@ const struct iwl_cfg iwl5165_2ac_cfg = {
.nvm_ver = IWL9000_NVM_VERSION, .nvm_ver = IWL9000_NVM_VERSION,
.nvm_calib_ver = IWL9000_TX_POWER_VERSION, .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
.integrated = true,
}; };
MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
......
...@@ -319,6 +319,7 @@ struct iwl_pwr_tx_backoff { ...@@ -319,6 +319,7 @@ struct iwl_pwr_tx_backoff {
* @mq_rx_supported: multi-queue rx support * @mq_rx_supported: multi-queue rx support
* @vht_mu_mimo_supported: VHT MU-MIMO support * @vht_mu_mimo_supported: VHT MU-MIMO support
* @rf_id: need to read rf_id to determine the firmware image * @rf_id: need to read rf_id to determine the firmware image
* @integrated: discrete or integrated
* *
* We enable the driver to be backward compatible wrt. hardware features. * We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs * API differences in uCode shouldn't be handled here but through TLVs
...@@ -362,7 +363,8 @@ struct iwl_cfg { ...@@ -362,7 +363,8 @@ struct iwl_cfg {
apmg_not_supported:1, apmg_not_supported:1,
mq_rx_supported:1, mq_rx_supported:1,
vht_mu_mimo_supported:1, vht_mu_mimo_supported:1,
rf_id:1; rf_id:1,
integrated:1;
u8 valid_tx_ant; u8 valid_tx_ant;
u8 valid_rx_ant; u8 valid_rx_ant;
u8 non_shared_ant; u8 non_shared_ant;
......
...@@ -145,8 +145,10 @@ ...@@ -145,8 +145,10 @@
#define CSR_LED_REG (CSR_BASE+0x094) #define CSR_LED_REG (CSR_BASE+0x094)
#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) #define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */ #define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE + 0x0A8) /* 6000 and up */
#define CSR_MAC_SHADOW_REG_CTRL_RX_WAKE BIT(20)
#define CSR_MAC_SHADOW_REG_CTL2 (CSR_BASE + 0x0AC)
#define CSR_MAC_SHADOW_REG_CTL2_RX_WAKE 0xFFFF
/* GIO Chicken Bits (PCI Express bus link power management) */ /* GIO Chicken Bits (PCI Express bus link power management) */
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
......
...@@ -673,4 +673,6 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) ...@@ -673,4 +673,6 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans); int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans);
int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans); int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans);
void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable);
#endif /* __iwl_trans_int_pcie_h__ */ #endif /* __iwl_trans_int_pcie_h__ */
...@@ -211,12 +211,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, ...@@ -211,12 +211,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
if (trans->cfg->mq_rx_supported) if (trans->cfg->mq_rx_supported)
iwl_write32(trans, RFH_Q_FRBDCB_WIDX_TRG(rxq->id), iwl_write32(trans, RFH_Q_FRBDCB_WIDX_TRG(rxq->id),
rxq->write_actual); rxq->write_actual);
/* else
* write to FH_RSCSR_CHNL0_WPTR register even in MQ as a W/A to iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
* hardware shadow registers bug - writing to RFH_Q_FRBDCB_WIDX will
* not wake the NIC.
*/
iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
} }
static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
...@@ -764,6 +760,23 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) ...@@ -764,6 +760,23 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE);
} }
void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable)
{
/*
* Turn on the chicken-bits that cause MAC wakeup for RX-related
* values.
* This costs some power, but needed for W/A 9000 integrated A-step
* bug where shadow registers are not in the retention list and their
* value is lost when NIC powers down
*/
if (trans->cfg->integrated) {
iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL,
CSR_MAC_SHADOW_REG_CTRL_RX_WAKE);
iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTL2,
CSR_MAC_SHADOW_REG_CTL2_RX_WAKE);
}
}
static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
...@@ -849,6 +862,8 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) ...@@ -849,6 +862,8 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
/* Set interrupt coalescing timer to default (2048 usecs) */ /* Set interrupt coalescing timer to default (2048 usecs) */
iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
iwl_pcie_enable_rx_wake(trans, true);
} }
static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq)
......
...@@ -1286,6 +1286,8 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, ...@@ -1286,6 +1286,8 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
iwl_clear_bit(trans, CSR_GP_CNTRL, iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_INIT_DONE); CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
iwl_pcie_enable_rx_wake(trans, false);
if (reset) { if (reset) {
/* /*
* reset TX queues -- some of their registers reset during S3 * reset TX queues -- some of their registers reset during S3
...@@ -1311,6 +1313,8 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, ...@@ -1311,6 +1313,8 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
return 0; return 0;
} }
iwl_pcie_enable_rx_wake(trans, true);
/* /*
* Also enables interrupts - none will happen as the device doesn't * Also enables interrupts - none will happen as the device doesn't
* know we're waking it up, only when the opmode actually tells it * know we're waking it up, only when the opmode actually tells it
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment