]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / ath / ath_hal / ar5416 / ar5416_interrupts.c
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  * $FreeBSD$
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         if (AR_SREV_HOWL(ah))
39                 return AH_TRUE;
40
41         /* 
42          * Some platforms trigger our ISR before applying power to
43          * the card, so make sure the INTPEND is really 1, not 0xffffffff.
44          */
45         isr = OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE);
46         if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_MAC_IRQ) != 0)
47                 return AH_TRUE;
48
49         isr = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
50         if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_SYNC_DEFAULT))
51                 return AH_TRUE;
52
53         return AH_FALSE;
54 }
55
56 /*
57  * Reads the Interrupt Status Register value from the NIC, thus deasserting
58  * the interrupt line, and returns both the masked and unmasked mapped ISR
59  * values.  The value returned is mapped to abstract the hw-specific bit
60  * locations in the Interrupt Status Register.
61  *
62  * (*masked) is cleared on initial call.
63  *
64  * Returns: A hardware-abstracted bitmap of all non-masked-out
65  *          interrupts pending, as well as an unmasked value
66  */
67 HAL_BOOL
68 ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked)
69 {
70         uint32_t isr, isr0, isr1, sync_cause = 0, o_sync_cause = 0;
71         HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
72
73 #ifdef  AH_INTERRUPT_DEBUGGING
74         /*
75          * Blank the interrupt debugging area regardless.
76          */
77         bzero(&ah->ah_intrstate, sizeof(ah->ah_intrstate));
78         ah->ah_syncstate = 0;
79 #endif
80
81         /*
82          * Verify there's a mac interrupt and the RTC is on.
83          */
84         if (AR_SREV_HOWL(ah)) {
85                 *masked = 0;
86                 isr = OS_REG_READ(ah, AR_ISR);
87         } else {
88                 if ((OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) &&
89                     (OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON)
90                         isr = OS_REG_READ(ah, AR_ISR);
91                 else
92                         isr = 0;
93 #ifdef  AH_INTERRUPT_DEBUGGING
94                 ah->ah_syncstate =
95 #endif
96                 o_sync_cause = sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
97                 sync_cause &= AR_INTR_SYNC_DEFAULT;
98                 *masked = 0;
99
100                 if (isr == 0 && sync_cause == 0)
101                         return AH_FALSE;
102         }
103
104 #ifdef  AH_INTERRUPT_DEBUGGING
105         ah->ah_intrstate[0] = isr;
106         ah->ah_intrstate[1] = OS_REG_READ(ah, AR_ISR_S0);
107         ah->ah_intrstate[2] = OS_REG_READ(ah, AR_ISR_S1);
108         ah->ah_intrstate[3] = OS_REG_READ(ah, AR_ISR_S2);
109         ah->ah_intrstate[4] = OS_REG_READ(ah, AR_ISR_S3);
110         ah->ah_intrstate[5] = OS_REG_READ(ah, AR_ISR_S4);
111         ah->ah_intrstate[6] = OS_REG_READ(ah, AR_ISR_S5);
112 #endif
113
114         if (isr != 0) {
115                 struct ath_hal_5212 *ahp = AH5212(ah);
116                 uint32_t mask2;
117
118                 mask2 = 0;
119                 if (isr & AR_ISR_BCNMISC) {
120                         uint32_t isr2 = OS_REG_READ(ah, AR_ISR_S2);
121                         if (isr2 & AR_ISR_S2_TIM)
122                                 mask2 |= HAL_INT_TIM;
123                         if (isr2 & AR_ISR_S2_DTIM)
124                                 mask2 |= HAL_INT_DTIM;
125                         if (isr2 & AR_ISR_S2_DTIMSYNC)
126                                 mask2 |= HAL_INT_DTIMSYNC;
127                         if (isr2 & (AR_ISR_S2_CABEND ))
128                                 mask2 |= HAL_INT_CABEND;
129                         if (isr2 & AR_ISR_S2_GTT)
130                                 mask2 |= HAL_INT_GTT;
131                         if (isr2 & AR_ISR_S2_CST)
132                                 mask2 |= HAL_INT_CST;   
133                         if (isr2 & AR_ISR_S2_TSFOOR)
134                                 mask2 |= HAL_INT_TSFOOR;
135
136                         /*
137                          * Don't mask out AR_BCNMISC; instead mask
138                          * out what causes it.
139                          */
140                         OS_REG_WRITE(ah, AR_ISR_S2, isr2);
141                         isr &= ~AR_ISR_BCNMISC;
142                 }
143
144                 if (isr == 0xffffffff) {
145                         *masked = 0;
146                         return AH_FALSE;
147                 }
148
149                 *masked = isr & HAL_INT_COMMON;
150
151                 if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
152                         *masked |= HAL_INT_RX;
153                 if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM))
154                         *masked |= HAL_INT_TX;
155
156                 /*
157                  * When doing RX interrupt mitigation, the RXOK bit is set
158                  * in AR_ISR even if the relevant bit in AR_IMR is clear.
159                  * Since this interrupt may be due to another source, don't
160                  * just automatically set HAL_INT_RX if it's set, otherwise
161                  * we could prematurely service the RX queue.
162                  *
163                  * In some cases, the driver can even handle all the RX
164                  * frames just before the mitigation interrupt fires.
165                  * The subsequent RX processing trip will then end up
166                  * processing 0 frames.
167                  */
168 #ifdef  AH_AR5416_INTERRUPT_MITIGATION
169                 if (isr & AR_ISR_RXERR)
170                         *masked |= HAL_INT_RX;
171 #else
172                 if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
173                         *masked |= HAL_INT_RX;
174 #endif
175
176                 if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR |
177                     AR_ISR_TXEOL)) {
178                         *masked |= HAL_INT_TX;
179
180                         isr0 = OS_REG_READ(ah, AR_ISR_S0);
181                         OS_REG_WRITE(ah, AR_ISR_S0, isr0);
182                         isr1 = OS_REG_READ(ah, AR_ISR_S1);
183                         OS_REG_WRITE(ah, AR_ISR_S1, isr1);
184
185                         /*
186                          * Don't clear the primary ISR TX bits, clear
187                          * what causes them (S0/S1.)
188                          */
189                         isr &= ~(AR_ISR_TXOK | AR_ISR_TXDESC |
190                             AR_ISR_TXERR | AR_ISR_TXEOL);
191
192                         ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK);
193                         ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC);
194                         ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR);
195                         ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL);
196                 }
197
198                 if ((isr & AR_ISR_GENTMR) || (! pCap->halAutoSleepSupport)) {
199                         uint32_t isr5;
200                         isr5 = OS_REG_READ(ah, AR_ISR_S5);
201                         OS_REG_WRITE(ah, AR_ISR_S5, isr5);
202                         isr &= ~AR_ISR_GENTMR;
203
204                         if (! pCap->halAutoSleepSupport)
205                                 if (isr5 & AR_ISR_S5_TIM_TIMER)
206                                         *masked |= HAL_INT_TIM_TIMER;
207                 }
208                 *masked |= mask2;
209         }
210
211         /*
212          * Since we're not using AR_ISR_RAC, clear the status bits
213          * for handled interrupts here. For bits whose interrupt
214          * source is a secondary register, those bits should've been
215          * masked out - instead of those bits being written back,
216          * their source (ie, the secondary status registers) should
217          * be cleared. That way there are no race conditions with
218          * new triggers coming in whilst they've been read/cleared.
219          */
220         OS_REG_WRITE(ah, AR_ISR, isr);
221         /* Flush previous write */
222         OS_REG_READ(ah, AR_ISR);
223
224         if (AR_SREV_HOWL(ah))
225                 return AH_TRUE;
226
227         if (sync_cause != 0) {
228                 HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: sync_cause=0x%x\n",
229                     __func__,
230                     o_sync_cause);
231                 if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) {
232                         *masked |= HAL_INT_FATAL;
233                 }
234                 if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
235                         HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RADM CPL timeout\n",
236                             __func__);
237                         OS_REG_WRITE(ah, AR_RC, AR_RC_HOSTIF);
238                         OS_REG_WRITE(ah, AR_RC, 0);
239                         *masked |= HAL_INT_FATAL;
240                 }
241                 /*
242                  * On fatal errors collect ISR state for debugging.
243                  */
244                 if (*masked & HAL_INT_FATAL) {
245                         AH_PRIVATE(ah)->ah_fatalState[0] = isr;
246                         AH_PRIVATE(ah)->ah_fatalState[1] = sync_cause;
247                         HALDEBUG(ah, HAL_DEBUG_ANY,
248                             "%s: fatal error, ISR_RAC 0x%x SYNC_CAUSE 0x%x\n",
249                             __func__, isr, sync_cause);
250                 }
251
252                 OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
253                 /* NB: flush write */
254                 (void) OS_REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR);
255         }
256         return AH_TRUE;
257 }
258
259 /*
260  * Atomically enables NIC interrupts.  Interrupts are passed in
261  * via the enumerated bitmask in ints.
262  */
263 HAL_INT
264 ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints)
265 {
266         struct ath_hal_5212 *ahp = AH5212(ah);
267         uint32_t omask = ahp->ah_maskReg;
268         uint32_t mask, mask2;
269
270         HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: 0x%x => 0x%x\n",
271             __func__, omask, ints);
272
273         if (omask & HAL_INT_GLOBAL) {
274                 HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: disable IER\n", __func__);
275                 OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
276                 (void) OS_REG_READ(ah, AR_IER);
277
278                 if (! AR_SREV_HOWL(ah)) {
279                         OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
280                         (void) OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE);
281
282                         OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
283                         (void) OS_REG_READ(ah, AR_INTR_SYNC_ENABLE);
284                 }
285         }
286
287         mask = ints & HAL_INT_COMMON;
288         mask2 = 0;
289
290 #ifdef  AH_AR5416_INTERRUPT_MITIGATION
291         /*
292          * Overwrite default mask if Interrupt mitigation
293          * is specified for AR5416
294          */
295         if (ints & HAL_INT_RX)
296                 mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM;
297 #else
298         if (ints & HAL_INT_RX)
299                 mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC;
300 #endif
301         if (ints & HAL_INT_TX) {
302                 if (ahp->ah_txOkInterruptMask)
303                         mask |= AR_IMR_TXOK;
304                 if (ahp->ah_txErrInterruptMask)
305                         mask |= AR_IMR_TXERR;
306                 if (ahp->ah_txDescInterruptMask)
307                         mask |= AR_IMR_TXDESC;
308                 if (ahp->ah_txEolInterruptMask)
309                         mask |= AR_IMR_TXEOL;
310                 if (ahp->ah_txUrnInterruptMask)
311                         mask |= AR_IMR_TXURN;
312         }
313         if (ints & (HAL_INT_BMISC)) {
314                 mask |= AR_IMR_BCNMISC;
315                 if (ints & HAL_INT_TIM)
316                         mask2 |= AR_IMR_S2_TIM;
317                 if (ints & HAL_INT_DTIM)
318                         mask2 |= AR_IMR_S2_DTIM;
319                 if (ints & HAL_INT_DTIMSYNC)
320                         mask2 |= AR_IMR_S2_DTIMSYNC;
321                 if (ints & HAL_INT_CABEND)
322                         mask2 |= (AR_IMR_S2_CABEND );
323                 if (ints & HAL_INT_CST)
324                         mask2 |= AR_IMR_S2_CST;
325                 if (ints & HAL_INT_TSFOOR)
326                         mask2 |= AR_IMR_S2_TSFOOR;
327         }
328
329         if (ints & (HAL_INT_GTT | HAL_INT_CST)) {
330                 mask |= AR_IMR_BCNMISC;
331                 if (ints & HAL_INT_GTT)
332                         mask2 |= AR_IMR_S2_GTT;
333                 if (ints & HAL_INT_CST)
334                         mask2 |= AR_IMR_S2_CST;
335         }
336
337         /* Write the new IMR and store off our SW copy. */
338         HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask);
339         OS_REG_WRITE(ah, AR_IMR, mask);
340         mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
341                                         AR_IMR_S2_DTIM |
342                                         AR_IMR_S2_DTIMSYNC |
343                                         AR_IMR_S2_CABEND |
344                                         AR_IMR_S2_CABTO  |
345                                         AR_IMR_S2_TSFOOR |
346                                         AR_IMR_S2_GTT |
347                                         AR_IMR_S2_CST);
348         OS_REG_WRITE(ah, AR_IMR_S2, mask | mask2);
349
350         ahp->ah_maskReg = ints;
351
352         /* Re-enable interrupts if they were enabled before. */
353         if (ints & HAL_INT_GLOBAL) {
354                 HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: enable IER\n", __func__);
355                 OS_REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
356
357                 if (! AR_SREV_HOWL(ah)) {
358                         mask = AR_INTR_MAC_IRQ;
359                         if (ints & HAL_INT_GPIO)
360                                 mask |= SM(AH5416(ah)->ah_gpioMask,
361                                     AR_INTR_ASYNC_MASK_GPIO);
362                         OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, mask);
363                         OS_REG_WRITE(ah, AR_INTR_ASYNC_MASK, mask);
364
365                         mask = AR_INTR_SYNC_DEFAULT;
366                         if (ints & HAL_INT_GPIO)
367                                 mask |= SM(AH5416(ah)->ah_gpioMask,
368                                     AR_INTR_SYNC_MASK_GPIO);
369                         OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, mask);
370                         OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, mask);
371                 }
372         }
373
374         return omask;
375 }