ath9k: Fix interrupt handling for the AR9002 family
This patch adds a driver workaround for a HW issue. A race condition in the HW results in missing interrupts, which can be avoided by a read/write with the ISR register. All chips in the AR9002 series are affected by this bug - AR9003 and above do not have this problem. Cc: stable@vger.kernel.org Cc: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
9278db6279
commit
73f0b56a1f
drivers/net/wireless/ath/ath9k
|
@ -76,9 +76,16 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
|
|||
mask2 |= ATH9K_INT_CST;
|
||||
if (isr2 & AR_ISR_S2_TSFOOR)
|
||||
mask2 |= ATH9K_INT_TSFOOR;
|
||||
|
||||
if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
|
||||
REG_WRITE(ah, AR_ISR_S2, isr2);
|
||||
isr &= ~AR_ISR_BCNMISC;
|
||||
}
|
||||
}
|
||||
|
||||
isr = REG_READ(ah, AR_ISR_RAC);
|
||||
if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)
|
||||
isr = REG_READ(ah, AR_ISR_RAC);
|
||||
|
||||
if (isr == 0xffffffff) {
|
||||
*masked = 0;
|
||||
return false;
|
||||
|
@ -97,11 +104,23 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
|
|||
|
||||
*masked |= ATH9K_INT_TX;
|
||||
|
||||
s0_s = REG_READ(ah, AR_ISR_S0_S);
|
||||
if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) {
|
||||
s0_s = REG_READ(ah, AR_ISR_S0_S);
|
||||
s1_s = REG_READ(ah, AR_ISR_S1_S);
|
||||
} else {
|
||||
s0_s = REG_READ(ah, AR_ISR_S0);
|
||||
REG_WRITE(ah, AR_ISR_S0, s0_s);
|
||||
s1_s = REG_READ(ah, AR_ISR_S1);
|
||||
REG_WRITE(ah, AR_ISR_S1, s1_s);
|
||||
|
||||
isr &= ~(AR_ISR_TXOK |
|
||||
AR_ISR_TXDESC |
|
||||
AR_ISR_TXERR |
|
||||
AR_ISR_TXEOL);
|
||||
}
|
||||
|
||||
ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK);
|
||||
ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC);
|
||||
|
||||
s1_s = REG_READ(ah, AR_ISR_S1_S);
|
||||
ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR);
|
||||
ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL);
|
||||
}
|
||||
|
@ -114,13 +133,15 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
|
|||
*masked |= mask2;
|
||||
}
|
||||
|
||||
if (AR_SREV_9100(ah))
|
||||
return true;
|
||||
|
||||
if (isr & AR_ISR_GENTMR) {
|
||||
if (!AR_SREV_9100(ah) && (isr & AR_ISR_GENTMR)) {
|
||||
u32 s5_s;
|
||||
|
||||
s5_s = REG_READ(ah, AR_ISR_S5_S);
|
||||
if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) {
|
||||
s5_s = REG_READ(ah, AR_ISR_S5_S);
|
||||
} else {
|
||||
s5_s = REG_READ(ah, AR_ISR_S5);
|
||||
}
|
||||
|
||||
ah->intr_gen_timer_trigger =
|
||||
MS(s5_s, AR_ISR_S5_GENTIMER_TRIG);
|
||||
|
||||
|
@ -133,8 +154,21 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
|
|||
if ((s5_s & AR_ISR_S5_TIM_TIMER) &&
|
||||
!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
|
||||
*masked |= ATH9K_INT_TIM_TIMER;
|
||||
|
||||
if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
|
||||
REG_WRITE(ah, AR_ISR_S5, s5_s);
|
||||
isr &= ~AR_ISR_GENTMR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
|
||||
REG_WRITE(ah, AR_ISR, isr);
|
||||
REG_READ(ah, AR_ISR);
|
||||
}
|
||||
|
||||
if (AR_SREV_9100(ah))
|
||||
return true;
|
||||
|
||||
if (sync_cause) {
|
||||
ath9k_debug_sync_cause(common, sync_cause);
|
||||
fatal_int =
|
||||
|
|
Loading…
Reference in New Issue