ar5416_interrupts.c revision 192397
1218887Sdim/*
2218887Sdim * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3218887Sdim * Copyright (c) 2002-2008 Atheros Communications, Inc.
4218887Sdim *
5218887Sdim * Permission to use, copy, modify, and/or distribute this software for any
6218887Sdim * purpose with or without fee is hereby granted, provided that the above
7218887Sdim * copyright notice and this permission notice appear in all copies.
8218887Sdim *
9218887Sdim * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10218887Sdim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11218887Sdim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12218887Sdim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13218887Sdim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14218887Sdim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15218887Sdim * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16221345Sdim *
17218887Sdim * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c 192397 2009-05-19 17:35:15Z sam $
18219077Sdim */
19218887Sdim#include "opt_ah.h"
20219077Sdim
21218887Sdim#include "ah.h"
22234353Sdim#include "ah_internal.h"
23234353Sdim
24218887Sdim#include "ar5416/ar5416.h"
25218887Sdim#include "ar5416/ar5416reg.h"
26218887Sdim
27218887Sdim/*
28218887Sdim * Checks to see if an interrupt is pending on our NIC
29218887Sdim *
30218887Sdim * Returns: TRUE    if an interrupt is pending
31218887Sdim *          FALSE   if not
32221345Sdim */
33234353SdimHAL_BOOL
34219077Sdimar5416IsInterruptPending(struct ath_hal *ah)
35218887Sdim{
36218887Sdim	uint32_t isr;
37224145Sdim	/*
38218887Sdim	 * Some platforms trigger our ISR before applying power to
39224145Sdim	 * the card, so make sure the INTPEND is really 1, not 0xffffffff.
40224145Sdim	 */
41234353Sdim	isr = OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE);
42224145Sdim	if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_MAC_IRQ) != 0)
43234353Sdim		return AH_TRUE;
44243830Sdim
45234353Sdim	isr = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
46234353Sdim	if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_SYNC_DEFAULT))
47224145Sdim		return AH_TRUE;
48224145Sdim
49224145Sdim	return AH_FALSE;
50234353Sdim}
51234353Sdim
52234353Sdim/*
53234353Sdim * Reads the Interrupt Status Register value from the NIC, thus deasserting
54234353Sdim * the interrupt line, and returns both the masked and unmasked mapped ISR
55234353Sdim * values.  The value returned is mapped to abstract the hw-specific bit
56234353Sdim * locations in the Interrupt Status Register.
57234353Sdim *
58234353Sdim * Returns: A hardware-abstracted bitmap of all non-masked-out
59234353Sdim *          interrupts pending, as well as an unmasked value
60218887Sdim */
61218887SdimHAL_BOOL
62218887Sdimar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked)
63218887Sdim{
64218887Sdim	uint32_t isr, isr0, isr1, sync_cause;
65218887Sdim
66218887Sdim	/*
67234353Sdim	 * Verify there's a mac interrupt and the RTC is on.
68224145Sdim	 */
69218887Sdim	if ((OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) &&
70218887Sdim	    (OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON)
71234353Sdim		isr = OS_REG_READ(ah, AR_ISR);
72218887Sdim	else
73218887Sdim		isr = 0;
74218887Sdim	sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
75218887Sdim	sync_cause &= AR_INTR_SYNC_DEFAULT;
76218887Sdim	if (isr == 0 && sync_cause == 0) {
77218887Sdim		*masked = 0;
78224145Sdim		return AH_FALSE;
79218887Sdim	}
80218887Sdim
81224145Sdim	if (isr != 0) {
82226633Sdim		struct ath_hal_5212 *ahp = AH5212(ah);
83226633Sdim		uint32_t mask2;
84224145Sdim
85218887Sdim		mask2 = 0;
86218887Sdim		if (isr & AR_ISR_BCNMISC) {
87218887Sdim			uint32_t isr2 = OS_REG_READ(ah, AR_ISR_S2);
88218887Sdim			if (isr2 & AR_ISR_S2_TIM)
89218887Sdim				mask2 |= HAL_INT_TIM;
90218887Sdim			if (isr2 & AR_ISR_S2_DTIM)
91218887Sdim				mask2 |= HAL_INT_DTIM;
92218887Sdim			if (isr2 & AR_ISR_S2_DTIMSYNC)
93218887Sdim				mask2 |= HAL_INT_DTIMSYNC;
94234353Sdim			if (isr2 & (AR_ISR_S2_CABEND ))
95218887Sdim				mask2 |= HAL_INT_CABEND;
96218887Sdim			if (isr2 & AR_ISR_S2_GTT)
97218887Sdim				mask2 |= HAL_INT_GTT;
98218887Sdim			if (isr2 & AR_ISR_S2_CST)
99218887Sdim				mask2 |= HAL_INT_CST;
100218887Sdim			if (isr2 & AR_ISR_S2_TSFOOR)
101218887Sdim				mask2 |= HAL_INT_TSFOOR;
102218887Sdim		}
103218887Sdim
104234353Sdim		isr = OS_REG_READ(ah, AR_ISR_RAC);
105218887Sdim		if (isr == 0xffffffff) {
106218887Sdim			*masked = 0;
107218887Sdim			return AH_FALSE;;
108218887Sdim		}
109218887Sdim
110218887Sdim		*masked = isr & HAL_INT_COMMON;
111218887Sdim		if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
112224145Sdim			*masked |= HAL_INT_RX;
113218887Sdim		if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL)) {
114218887Sdim			*masked |= HAL_INT_TX;
115218887Sdim			isr0 = OS_REG_READ(ah, AR_ISR_S0_S);
116218887Sdim			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK);
117218887Sdim			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC);
118218887Sdim			isr1 = OS_REG_READ(ah, AR_ISR_S1_S);
119218887Sdim			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR);
120218887Sdim			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL);
121218887Sdim		}
122234353Sdim
123218887Sdim		/* Interrupt Mitigation on AR5416 */
124218887Sdim#ifdef AR5416_INT_MITIGATION
125218887Sdim		if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
126218887Sdim			*masked |= HAL_INT_RX;
127218887Sdim		if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM))
128218887Sdim			*masked |= HAL_INT_TX;
129218887Sdim#endif
130218887Sdim		*masked |= mask2;
131218887Sdim	}
132218887Sdim	if (sync_cause != 0) {
133218887Sdim		if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) {
134218887Sdim			*masked |= HAL_INT_FATAL;
135224145Sdim		}
136224145Sdim		if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
137226633Sdim			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RADM CPL timeout\n",
138226633Sdim			    __func__);
139218887Sdim			OS_REG_WRITE(ah, AR_RC, AR_RC_HOSTIF);
140218887Sdim			OS_REG_WRITE(ah, AR_RC, 0);
141218887Sdim			*masked |= HAL_INT_FATAL;
142243830Sdim		}
143218887Sdim		/*
144218887Sdim		 * On fatal errors collect ISR state for debugging.
145218887Sdim		 */
146218887Sdim		if (*masked & HAL_INT_FATAL) {
147218887Sdim			AH_PRIVATE(ah)->ah_fatalState[0] = isr;
148218887Sdim			AH_PRIVATE(ah)->ah_fatalState[1] = sync_cause;
149218887Sdim			HALDEBUG(ah, HAL_DEBUG_ANY,
150224145Sdim			    "%s: fatal error, ISR_RAC 0x%x SYNC_CAUSE 0x%x\n",
151224145Sdim			    __func__, isr, sync_cause);
152218887Sdim		}
153218887Sdim
154218887Sdim		OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
155218887Sdim		/* NB: flush write */
156218887Sdim		(void) OS_REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR);
157218887Sdim	}
158218887Sdim	return AH_TRUE;
159218887Sdim}
160218887Sdim
161234353Sdim/*
162234353Sdim * Atomically enables NIC interrupts.  Interrupts are passed in
163234353Sdim * via the enumerated bitmask in ints.
164218887Sdim */
165218887SdimHAL_INT
166218887Sdimar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints)
167218887Sdim{
168218887Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
169218887Sdim	uint32_t omask = ahp->ah_maskReg;
170218887Sdim	uint32_t mask, mask2;
171234353Sdim
172218887Sdim	HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: 0x%x => 0x%x\n",
173218887Sdim	    __func__, omask, ints);
174218887Sdim
175218887Sdim	if (omask & HAL_INT_GLOBAL) {
176218887Sdim		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: disable IER\n", __func__);
177218887Sdim		OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
178218887Sdim		(void) OS_REG_READ(ah, AR_IER);
179218887Sdim
180218887Sdim		OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
181218887Sdim		(void) OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE);
182218887Sdim
183224145Sdim		OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
184224145Sdim		(void) OS_REG_READ(ah, AR_INTR_SYNC_ENABLE);
185226633Sdim	}
186218887Sdim
187243830Sdim	mask = ints & HAL_INT_COMMON;
188218887Sdim	mask2 = 0;
189218887Sdim
190218887Sdim	if (ints & HAL_INT_TX) {
191243830Sdim		if (ahp->ah_txOkInterruptMask)
192243830Sdim			mask |= AR_IMR_TXOK;
193218887Sdim		if (ahp->ah_txErrInterruptMask)
194234353Sdim			mask |= AR_IMR_TXERR;
195234353Sdim		if (ahp->ah_txDescInterruptMask)
196218887Sdim			mask |= AR_IMR_TXDESC;
197234353Sdim		if (ahp->ah_txEolInterruptMask)
198234353Sdim			mask |= AR_IMR_TXEOL;
199234353Sdim	}
200234353Sdim	if (ints & HAL_INT_RX)
201234353Sdim		mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC;
202234353Sdim#ifdef AR5416_INT_MITIGATION
203234353Sdim	/*
204234353Sdim	 * Overwrite default mask if Interrupt mitigation
205234353Sdim	 * is specified for AR5416
206234353Sdim	 */
207234353Sdim	mask = ints & HAL_INT_COMMON;
208218887Sdim	if (ints & HAL_INT_TX)
209234353Sdim		mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM;
210234353Sdim	if (ints & HAL_INT_RX)
211234353Sdim		mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM;
212234353Sdim#endif
213234353Sdim	if (ints & (HAL_INT_BMISC)) {
214234353Sdim		mask |= AR_IMR_BCNMISC;
215234353Sdim		if (ints & HAL_INT_TIM)
216234353Sdim			mask2 |= AR_IMR_S2_TIM;
217234353Sdim		if (ints & HAL_INT_DTIM)
218234353Sdim			mask2 |= AR_IMR_S2_DTIM;
219234353Sdim		if (ints & HAL_INT_DTIMSYNC)
220234353Sdim			mask2 |= AR_IMR_S2_DTIMSYNC;
221234353Sdim		if (ints & HAL_INT_CABEND)
222234353Sdim			mask2 |= (AR_IMR_S2_CABEND );
223234353Sdim		if (ints & HAL_INT_GTT)
224234353Sdim			mask2 |= AR_IMR_S2_GTT;
225234353Sdim		if (ints & HAL_INT_CST)
226234353Sdim			mask2 |= AR_IMR_S2_CST;
227234353Sdim		if (ints & HAL_INT_TSFOOR)
228234353Sdim			mask2 |= AR_IMR_S2_TSFOOR;
229243830Sdim	}
230243830Sdim
231234353Sdim	/* Write the new IMR and store off our SW copy. */
232234353Sdim	HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask);
233234353Sdim	OS_REG_WRITE(ah, AR_IMR, mask);
234234353Sdim	mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
235234353Sdim					AR_IMR_S2_DTIM |
236234353Sdim					AR_IMR_S2_DTIMSYNC |
237234353Sdim					AR_IMR_S2_CABEND |
238234353Sdim					AR_IMR_S2_CABTO  |
239234353Sdim					AR_IMR_S2_TSFOOR |
240234353Sdim					AR_IMR_S2_GTT |
241234353Sdim					AR_IMR_S2_CST);
242234353Sdim	OS_REG_WRITE(ah, AR_IMR_S2, mask | mask2);
243234353Sdim
244218887Sdim	ahp->ah_maskReg = ints;
245218887Sdim
246218887Sdim	/* Re-enable interrupts if they were enabled before. */
247234353Sdim	if (ints & HAL_INT_GLOBAL) {
248234353Sdim		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: enable IER\n", __func__);
249234353Sdim		OS_REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
250234353Sdim
251218887Sdim		mask = AR_INTR_MAC_IRQ;
252218887Sdim		if (ints & HAL_INT_GPIO)
253218887Sdim			mask |= SM(AH5416(ah)->ah_gpioMask,
254234353Sdim			    AR_INTR_ASYNC_MASK_GPIO);
255218887Sdim		OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, mask);
256234353Sdim		OS_REG_WRITE(ah, AR_INTR_ASYNC_MASK, mask);
257234353Sdim
258234353Sdim		mask = AR_INTR_SYNC_DEFAULT;
259234353Sdim		if (ints & HAL_INT_GPIO)
260239462Sdim			mask |= SM(AH5416(ah)->ah_gpioMask,
261234353Sdim			    AR_INTR_SYNC_MASK_GPIO);
262234353Sdim		OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, mask);
263234353Sdim		OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, mask);
264234353Sdim	}
265218887Sdim
266234353Sdim	return omask;
267234353Sdim}
268234353Sdim