diff options
| author | Sujith Manoharan <c_manoha@qca.qualcomm.com> | 2013-12-15 20:34:59 -0500 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2013-12-17 13:58:25 -0500 |
| commit | 73f0b56a1ff64e7fb6c3a62088804bab93bcedc2 (patch) | |
| tree | 94ae80d59e00f1dcc52f7795a8c22d456cc469de | |
| parent | 9278db6279e28d4d433bc8a848e10b4ece8793ed (diff) | |
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>
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9002_mac.c | 52 |
1 files changed, 43 insertions, 9 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 8d78253c26ce..a366d6b4626f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c | |||
| @@ -76,9 +76,16 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) | |||
| 76 | mask2 |= ATH9K_INT_CST; | 76 | mask2 |= ATH9K_INT_CST; |
| 77 | if (isr2 & AR_ISR_S2_TSFOOR) | 77 | if (isr2 & AR_ISR_S2_TSFOOR) |
| 78 | mask2 |= ATH9K_INT_TSFOOR; | 78 | mask2 |= ATH9K_INT_TSFOOR; |
| 79 | |||
| 80 | if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { | ||
| 81 | REG_WRITE(ah, AR_ISR_S2, isr2); | ||
| 82 | isr &= ~AR_ISR_BCNMISC; | ||
| 83 | } | ||
| 79 | } | 84 | } |
| 80 | 85 | ||
| 81 | isr = REG_READ(ah, AR_ISR_RAC); | 86 | if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) |
| 87 | isr = REG_READ(ah, AR_ISR_RAC); | ||
| 88 | |||
| 82 | if (isr == 0xffffffff) { | 89 | if (isr == 0xffffffff) { |
| 83 | *masked = 0; | 90 | *masked = 0; |
| 84 | return false; | 91 | return false; |
| @@ -97,11 +104,23 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) | |||
| 97 | 104 | ||
| 98 | *masked |= ATH9K_INT_TX; | 105 | *masked |= ATH9K_INT_TX; |
| 99 | 106 | ||
| 100 | s0_s = REG_READ(ah, AR_ISR_S0_S); | 107 | if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) { |
| 108 | s0_s = REG_READ(ah, AR_ISR_S0_S); | ||
| 109 | s1_s = REG_READ(ah, AR_ISR_S1_S); | ||
| 110 | } else { | ||
| 111 | s0_s = REG_READ(ah, AR_ISR_S0); | ||
| 112 | REG_WRITE(ah, AR_ISR_S0, s0_s); | ||
| 113 | s1_s = REG_READ(ah, AR_ISR_S1); | ||
| 114 | REG_WRITE(ah, AR_ISR_S1, s1_s); | ||
| 115 | |||
| 116 | isr &= ~(AR_ISR_TXOK | | ||
| 117 | AR_ISR_TXDESC | | ||
| 118 | AR_ISR_TXERR | | ||
| 119 | AR_ISR_TXEOL); | ||
| 120 | } | ||
| 121 | |||
| 101 | ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); | 122 | ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); |
| 102 | ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); | 123 | ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); |
| 103 | |||
| 104 | s1_s = REG_READ(ah, AR_ISR_S1_S); | ||
| 105 | ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); | 124 | ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); |
| 106 | ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); | 125 | ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); |
| 107 | } | 126 | } |
| @@ -114,13 +133,15 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) | |||
| 114 | *masked |= mask2; | 133 | *masked |= mask2; |
| 115 | } | 134 | } |
| 116 | 135 | ||
| 117 | if (AR_SREV_9100(ah)) | 136 | if (!AR_SREV_9100(ah) && (isr & AR_ISR_GENTMR)) { |
| 118 | return true; | ||
| 119 | |||
| 120 | if (isr & AR_ISR_GENTMR) { | ||
| 121 | u32 s5_s; | 137 | u32 s5_s; |
| 122 | 138 | ||
| 123 | s5_s = REG_READ(ah, AR_ISR_S5_S); | 139 | if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) { |
| 140 | s5_s = REG_READ(ah, AR_ISR_S5_S); | ||
| 141 | } else { | ||
| 142 | s5_s = REG_READ(ah, AR_ISR_S5); | ||
| 143 | } | ||
| 144 | |||
| 124 | ah->intr_gen_timer_trigger = | 145 | ah->intr_gen_timer_trigger = |
| 125 | MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); | 146 | MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); |
| 126 | 147 | ||
| @@ -133,8 +154,21 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) | |||
| 133 | if ((s5_s & AR_ISR_S5_TIM_TIMER) && | 154 | if ((s5_s & AR_ISR_S5_TIM_TIMER) && |
| 134 | !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) | 155 | !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) |
| 135 | *masked |= ATH9K_INT_TIM_TIMER; | 156 | *masked |= ATH9K_INT_TIM_TIMER; |
| 157 | |||
| 158 | if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { | ||
| 159 | REG_WRITE(ah, AR_ISR_S5, s5_s); | ||
| 160 | isr &= ~AR_ISR_GENTMR; | ||
| 161 | } | ||
| 136 | } | 162 | } |
| 137 | 163 | ||
| 164 | if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { | ||
| 165 | REG_WRITE(ah, AR_ISR, isr); | ||
| 166 | REG_READ(ah, AR_ISR); | ||
| 167 | } | ||
| 168 | |||
| 169 | if (AR_SREV_9100(ah)) | ||
| 170 | return true; | ||
| 171 | |||
| 138 | if (sync_cause) { | 172 | if (sync_cause) { |
| 139 | ath9k_debug_sync_cause(common, sync_cause); | 173 | ath9k_debug_sync_cause(common, sync_cause); |
| 140 | fatal_int = | 174 | fatal_int = |
