1 | /* |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
3 | * Copyright (c) 2002-2008 Atheros Communications, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | * $Id: ar5416_interrupts.c,v 1.3 2011/11/28 00:30:17 jmcneill Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | |
24 | #include "ar5416/ar5416.h" |
25 | #include "ar5416/ar5416reg.h" |
26 | |
27 | /* |
28 | * Checks to see if an interrupt is pending on our NIC |
29 | * |
30 | * Returns: TRUE if an interrupt is pending |
31 | * FALSE if not |
32 | */ |
33 | HAL_BOOL |
34 | ar5416IsInterruptPending(struct ath_hal *ah) |
35 | { |
36 | uint32_t isr; |
37 | /* |
38 | * Some platforms trigger our ISR before applying power to |
39 | * the card, so make sure the INTPEND is really 1, not 0xffffffff. |
40 | */ |
41 | isr = OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE); |
42 | if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_MAC_IRQ) != 0) |
43 | return AH_TRUE; |
44 | |
45 | isr = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); |
46 | if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_SYNC_DEFAULT)) |
47 | return AH_TRUE; |
48 | |
49 | return AH_FALSE; |
50 | } |
51 | |
52 | /* |
53 | * Reads the Interrupt Status Register value from the NIC, thus deasserting |
54 | * the interrupt line, and returns both the masked and unmasked mapped ISR |
55 | * values. The value returned is mapped to abstract the hw-specific bit |
56 | * locations in the Interrupt Status Register. |
57 | * |
58 | * (*masked) is cleared on initial call. |
59 | * |
60 | * Returns: A hardware-abstracted bitmap of all non-masked-out |
61 | * interrupts pending, as well as an unmasked value |
62 | */ |
63 | HAL_BOOL |
64 | ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) |
65 | { |
66 | uint32_t isr, isr0, isr1, sync_cause; |
67 | |
68 | /* |
69 | * Verify there's a mac interrupt and the RTC is on. |
70 | */ |
71 | if ((OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) && |
72 | (OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) |
73 | isr = OS_REG_READ(ah, AR_ISR); |
74 | else |
75 | isr = 0; |
76 | sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); |
77 | sync_cause &= AR_INTR_SYNC_DEFAULT; |
78 | *masked = 0; |
79 | if (isr == 0 && sync_cause == 0) |
80 | return AH_FALSE; |
81 | |
82 | if (isr != 0) { |
83 | struct ath_hal_5212 *ahp = AH5212(ah); |
84 | uint32_t mask2; |
85 | |
86 | mask2 = 0; |
87 | if (isr & AR_ISR_BCNMISC) { |
88 | uint32_t isr2 = OS_REG_READ(ah, AR_ISR_S2); |
89 | if (isr2 & AR_ISR_S2_TIM) |
90 | mask2 |= HAL_INT_TIM; |
91 | if (isr2 & AR_ISR_S2_DTIM) |
92 | mask2 |= HAL_INT_DTIM; |
93 | if (isr2 & AR_ISR_S2_DTIMSYNC) |
94 | mask2 |= HAL_INT_DTIMSYNC; |
95 | if (isr2 & (AR_ISR_S2_CABEND )) |
96 | mask2 |= HAL_INT_CABEND; |
97 | if (isr2 & AR_ISR_S2_GTT) |
98 | mask2 |= HAL_INT_GTT; |
99 | if (isr2 & AR_ISR_S2_CST) |
100 | mask2 |= HAL_INT_CST; |
101 | if (isr2 & AR_ISR_S2_TSFOOR) |
102 | mask2 |= HAL_INT_TSFOOR; |
103 | } |
104 | |
105 | isr = OS_REG_READ(ah, AR_ISR_RAC); |
106 | if (isr == 0xffffffff) { |
107 | *masked = 0; |
108 | return AH_FALSE; |
109 | } |
110 | |
111 | *masked = isr & HAL_INT_COMMON; |
112 | if (isr & (AR_ISR_RXOK | AR_ISR_RXERR)) |
113 | *masked |= HAL_INT_RX; |
114 | if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL)) { |
115 | *masked |= HAL_INT_TX; |
116 | isr0 = OS_REG_READ(ah, AR_ISR_S0_S); |
117 | ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK); |
118 | ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC); |
119 | isr1 = OS_REG_READ(ah, AR_ISR_S1_S); |
120 | ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR); |
121 | ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL); |
122 | } |
123 | |
124 | if (AR_SREV_MERLIN(ah) || AR_SREV_KITE(ah)) { |
125 | uint32_t isr5; |
126 | isr5 = OS_REG_READ(ah, AR_ISR_S5_S); |
127 | if (isr5 & AR_ISR_S5_TIM_TIMER) |
128 | *masked |= HAL_INT_TIM_TIMER; |
129 | } |
130 | |
131 | /* Interrupt Mitigation on AR5416 */ |
132 | #ifdef AR5416_INT_MITIGATION |
133 | if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) |
134 | *masked |= HAL_INT_RX; |
135 | if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM)) |
136 | *masked |= HAL_INT_TX; |
137 | #endif |
138 | *masked |= mask2; |
139 | } |
140 | if (sync_cause != 0) { |
141 | if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) { |
142 | *masked |= HAL_INT_FATAL; |
143 | } |
144 | if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { |
145 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RADM CPL timeout\n" , |
146 | __func__); |
147 | OS_REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); |
148 | OS_REG_WRITE(ah, AR_RC, 0); |
149 | *masked |= HAL_INT_FATAL; |
150 | } |
151 | /* |
152 | * On fatal errors collect ISR state for debugging. |
153 | */ |
154 | if (*masked & HAL_INT_FATAL) { |
155 | AH_PRIVATE(ah)->ah_fatalState[0] = isr; |
156 | AH_PRIVATE(ah)->ah_fatalState[1] = sync_cause; |
157 | HALDEBUG(ah, HAL_DEBUG_ANY, |
158 | "%s: fatal error, ISR_RAC 0x%x SYNC_CAUSE 0x%x\n" , |
159 | __func__, isr, sync_cause); |
160 | } |
161 | |
162 | OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); |
163 | /* NB: flush write */ |
164 | (void) OS_REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); |
165 | } |
166 | return AH_TRUE; |
167 | } |
168 | |
169 | /* |
170 | * Atomically enables NIC interrupts. Interrupts are passed in |
171 | * via the enumerated bitmask in ints. |
172 | */ |
173 | HAL_INT |
174 | ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) |
175 | { |
176 | struct ath_hal_5212 *ahp = AH5212(ah); |
177 | uint32_t omask = ahp->ah_maskReg; |
178 | uint32_t mask, mask2; |
179 | |
180 | HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: 0x%x => 0x%x\n" , |
181 | __func__, omask, ints); |
182 | |
183 | if (omask & HAL_INT_GLOBAL) { |
184 | HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: disable IER\n" , __func__); |
185 | OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE); |
186 | (void) OS_REG_READ(ah, AR_IER); |
187 | |
188 | OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0); |
189 | (void) OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE); |
190 | |
191 | OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); |
192 | (void) OS_REG_READ(ah, AR_INTR_SYNC_ENABLE); |
193 | } |
194 | |
195 | mask = ints & HAL_INT_COMMON; |
196 | mask2 = 0; |
197 | |
198 | if (ints & HAL_INT_TX) { |
199 | if (ahp->ah_txOkInterruptMask) |
200 | mask |= AR_IMR_TXOK; |
201 | if (ahp->ah_txErrInterruptMask) |
202 | mask |= AR_IMR_TXERR; |
203 | if (ahp->ah_txDescInterruptMask) |
204 | mask |= AR_IMR_TXDESC; |
205 | if (ahp->ah_txEolInterruptMask) |
206 | mask |= AR_IMR_TXEOL; |
207 | } |
208 | if (ints & HAL_INT_RX) |
209 | mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC; |
210 | #ifdef AR5416_INT_MITIGATION |
211 | /* |
212 | * Overwrite default mask if Interrupt mitigation |
213 | * is specified for AR5416 |
214 | */ |
215 | mask = ints & HAL_INT_COMMON; |
216 | if (ints & HAL_INT_TX) |
217 | mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM; |
218 | if (ints & HAL_INT_RX) |
219 | mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM; |
220 | #endif |
221 | if (ints & (HAL_INT_BMISC)) { |
222 | mask |= AR_IMR_BCNMISC; |
223 | if (ints & HAL_INT_TIM) |
224 | mask2 |= AR_IMR_S2_TIM; |
225 | if (ints & HAL_INT_DTIM) |
226 | mask2 |= AR_IMR_S2_DTIM; |
227 | if (ints & HAL_INT_DTIMSYNC) |
228 | mask2 |= AR_IMR_S2_DTIMSYNC; |
229 | if (ints & HAL_INT_CABEND) |
230 | mask2 |= (AR_IMR_S2_CABEND ); |
231 | if (ints & HAL_INT_GTT) |
232 | mask2 |= AR_IMR_S2_GTT; |
233 | if (ints & HAL_INT_CST) |
234 | mask2 |= AR_IMR_S2_CST; |
235 | if (ints & HAL_INT_TSFOOR) |
236 | mask2 |= AR_IMR_S2_TSFOOR; |
237 | } |
238 | |
239 | /* Write the new IMR and store off our SW copy. */ |
240 | HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n" , __func__, mask); |
241 | OS_REG_WRITE(ah, AR_IMR, mask); |
242 | mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | |
243 | AR_IMR_S2_DTIM | |
244 | AR_IMR_S2_DTIMSYNC | |
245 | AR_IMR_S2_CABEND | |
246 | AR_IMR_S2_CABTO | |
247 | AR_IMR_S2_TSFOOR | |
248 | AR_IMR_S2_GTT | |
249 | AR_IMR_S2_CST); |
250 | OS_REG_WRITE(ah, AR_IMR_S2, mask | mask2); |
251 | |
252 | ahp->ah_maskReg = ints; |
253 | |
254 | /* Re-enable interrupts if they were enabled before. */ |
255 | if (ints & HAL_INT_GLOBAL) { |
256 | HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: enable IER\n" , __func__); |
257 | OS_REG_WRITE(ah, AR_IER, AR_IER_ENABLE); |
258 | |
259 | OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, AR_INTR_MAC_IRQ); |
260 | OS_REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ); |
261 | |
262 | OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT); |
263 | OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, AR_INTR_SYNC_DEFAULT); |
264 | } |
265 | |
266 | return omask; |
267 | } |
268 | |