1185377Ssam/*
2185377Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3185377Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185377Ssam *
5185377Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185377Ssam * purpose with or without fee is hereby granted, provided that the above
7185377Ssam * copyright notice and this permission notice appear in all copies.
8185377Ssam *
9185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185377Ssam *
17188976Ssam * $FreeBSD: releng/10.3/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c 247030 2013-02-20 11:24:11Z adrian $
18185377Ssam */
19185377Ssam#include "opt_ah.h"
20185377Ssam
21185377Ssam#include "ah.h"
22185377Ssam#include "ah_internal.h"
23185377Ssam
24185377Ssam#include "ar5416/ar5416.h"
25185377Ssam#include "ar5416/ar5416reg.h"
26185377Ssam
27185377Ssam/*
28185377Ssam * Checks to see if an interrupt is pending on our NIC
29185377Ssam *
30185377Ssam * Returns: TRUE    if an interrupt is pending
31185377Ssam *          FALSE   if not
32185377Ssam */
33185377SsamHAL_BOOL
34185377Ssamar5416IsInterruptPending(struct ath_hal *ah)
35185377Ssam{
36185377Ssam	uint32_t isr;
37221163Sadrian
38221163Sadrian	if (AR_SREV_HOWL(ah))
39221163Sadrian		return AH_TRUE;
40221163Sadrian
41185377Ssam	/*
42185377Ssam	 * Some platforms trigger our ISR before applying power to
43185377Ssam	 * the card, so make sure the INTPEND is really 1, not 0xffffffff.
44185377Ssam	 */
45185377Ssam	isr = OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE);
46185377Ssam	if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_MAC_IRQ) != 0)
47185377Ssam		return AH_TRUE;
48185377Ssam
49185377Ssam	isr = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
50185377Ssam	if (isr != AR_INTR_SPURIOUS && (isr & AR_INTR_SYNC_DEFAULT))
51185377Ssam		return AH_TRUE;
52185377Ssam
53185377Ssam	return AH_FALSE;
54185377Ssam}
55185377Ssam
56185377Ssam/*
57185377Ssam * Reads the Interrupt Status Register value from the NIC, thus deasserting
58185377Ssam * the interrupt line, and returns both the masked and unmasked mapped ISR
59185377Ssam * values.  The value returned is mapped to abstract the hw-specific bit
60185377Ssam * locations in the Interrupt Status Register.
61185377Ssam *
62220966Sadrian * (*masked) is cleared on initial call.
63220966Sadrian *
64185377Ssam * Returns: A hardware-abstracted bitmap of all non-masked-out
65185377Ssam *          interrupts pending, as well as an unmasked value
66185377Ssam */
67185377SsamHAL_BOOL
68185377Ssamar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked)
69185377Ssam{
70233900Sadrian	uint32_t isr, isr0, isr1, sync_cause = 0, o_sync_cause = 0;
71225925Sadrian	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
72185377Ssam
73227365Sadrian#ifdef	AH_INTERRUPT_DEBUGGING
74185377Ssam	/*
75227365Sadrian	 * Blank the interrupt debugging area regardless.
76227365Sadrian	 */
77227365Sadrian	bzero(&ah->ah_intrstate, sizeof(ah->ah_intrstate));
78234088Sadrian	ah->ah_syncstate = 0;
79227365Sadrian#endif
80227365Sadrian
81227365Sadrian	/*
82185377Ssam	 * Verify there's a mac interrupt and the RTC is on.
83185377Ssam	 */
84221163Sadrian	if (AR_SREV_HOWL(ah)) {
85221163Sadrian		*masked = 0;
86185377Ssam		isr = OS_REG_READ(ah, AR_ISR);
87221163Sadrian	} else {
88221163Sadrian		if ((OS_REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) &&
89221163Sadrian		    (OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON)
90221163Sadrian			isr = OS_REG_READ(ah, AR_ISR);
91221163Sadrian		else
92221163Sadrian			isr = 0;
93234088Sadrian#ifdef	AH_INTERRUPT_DEBUGGING
94234088Sadrian		ah->ah_syncstate =
95234088Sadrian#endif
96233900Sadrian		o_sync_cause = sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
97221163Sadrian		sync_cause &= AR_INTR_SYNC_DEFAULT;
98221163Sadrian		*masked = 0;
99220966Sadrian
100221163Sadrian		if (isr == 0 && sync_cause == 0)
101221163Sadrian			return AH_FALSE;
102221163Sadrian	}
103185377Ssam
104227365Sadrian#ifdef	AH_INTERRUPT_DEBUGGING
105227365Sadrian	ah->ah_intrstate[0] = isr;
106227365Sadrian	ah->ah_intrstate[1] = OS_REG_READ(ah, AR_ISR_S0);
107227365Sadrian	ah->ah_intrstate[2] = OS_REG_READ(ah, AR_ISR_S1);
108227365Sadrian	ah->ah_intrstate[3] = OS_REG_READ(ah, AR_ISR_S2);
109227365Sadrian	ah->ah_intrstate[4] = OS_REG_READ(ah, AR_ISR_S3);
110227365Sadrian	ah->ah_intrstate[5] = OS_REG_READ(ah, AR_ISR_S4);
111227365Sadrian	ah->ah_intrstate[6] = OS_REG_READ(ah, AR_ISR_S5);
112227365Sadrian#endif
113227365Sadrian
114185377Ssam	if (isr != 0) {
115185377Ssam		struct ath_hal_5212 *ahp = AH5212(ah);
116185377Ssam		uint32_t mask2;
117185377Ssam
118185377Ssam		mask2 = 0;
119185377Ssam		if (isr & AR_ISR_BCNMISC) {
120185377Ssam			uint32_t isr2 = OS_REG_READ(ah, AR_ISR_S2);
121185377Ssam			if (isr2 & AR_ISR_S2_TIM)
122185377Ssam				mask2 |= HAL_INT_TIM;
123185377Ssam			if (isr2 & AR_ISR_S2_DTIM)
124185377Ssam				mask2 |= HAL_INT_DTIM;
125185377Ssam			if (isr2 & AR_ISR_S2_DTIMSYNC)
126185377Ssam				mask2 |= HAL_INT_DTIMSYNC;
127185377Ssam			if (isr2 & (AR_ISR_S2_CABEND ))
128185377Ssam				mask2 |= HAL_INT_CABEND;
129185377Ssam			if (isr2 & AR_ISR_S2_GTT)
130185377Ssam				mask2 |= HAL_INT_GTT;
131185377Ssam			if (isr2 & AR_ISR_S2_CST)
132185377Ssam				mask2 |= HAL_INT_CST;
133185377Ssam			if (isr2 & AR_ISR_S2_TSFOOR)
134185377Ssam				mask2 |= HAL_INT_TSFOOR;
135225925Sadrian
136225925Sadrian			/*
137225925Sadrian			 * Don't mask out AR_BCNMISC; instead mask
138225925Sadrian			 * out what causes it.
139225925Sadrian			 */
140225925Sadrian			OS_REG_WRITE(ah, AR_ISR_S2, isr2);
141225925Sadrian			isr &= ~AR_ISR_BCNMISC;
142185377Ssam		}
143185377Ssam
144185377Ssam		if (isr == 0xffffffff) {
145185377Ssam			*masked = 0;
146201758Smbr			return AH_FALSE;
147185377Ssam		}
148185377Ssam
149185377Ssam		*masked = isr & HAL_INT_COMMON;
150225925Sadrian
151225925Sadrian		if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
152225925Sadrian			*masked |= HAL_INT_RX;
153225925Sadrian		if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM))
154225925Sadrian			*masked |= HAL_INT_TX;
155225925Sadrian
156225925Sadrian		/*
157225925Sadrian		 * When doing RX interrupt mitigation, the RXOK bit is set
158225925Sadrian		 * in AR_ISR even if the relevant bit in AR_IMR is clear.
159225925Sadrian		 * Since this interrupt may be due to another source, don't
160225925Sadrian		 * just automatically set HAL_INT_RX if it's set, otherwise
161225925Sadrian		 * we could prematurely service the RX queue.
162225925Sadrian		 *
163225925Sadrian		 * In some cases, the driver can even handle all the RX
164225925Sadrian		 * frames just before the mitigation interrupt fires.
165225925Sadrian		 * The subsequent RX processing trip will then end up
166225925Sadrian		 * processing 0 frames.
167225925Sadrian		 */
168225925Sadrian#ifdef	AH_AR5416_INTERRUPT_MITIGATION
169225925Sadrian		if (isr & AR_ISR_RXERR)
170225925Sadrian			*masked |= HAL_INT_RX;
171225925Sadrian#else
172185377Ssam		if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
173185377Ssam			*masked |= HAL_INT_RX;
174225925Sadrian#endif
175225925Sadrian
176225925Sadrian		if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR |
177225925Sadrian		    AR_ISR_TXEOL)) {
178185377Ssam			*masked |= HAL_INT_TX;
179225925Sadrian
180225925Sadrian			isr0 = OS_REG_READ(ah, AR_ISR_S0);
181225925Sadrian			OS_REG_WRITE(ah, AR_ISR_S0, isr0);
182225925Sadrian			isr1 = OS_REG_READ(ah, AR_ISR_S1);
183225925Sadrian			OS_REG_WRITE(ah, AR_ISR_S1, isr1);
184225925Sadrian
185225925Sadrian			/*
186225925Sadrian			 * Don't clear the primary ISR TX bits, clear
187225925Sadrian			 * what causes them (S0/S1.)
188225925Sadrian			 */
189225925Sadrian			isr &= ~(AR_ISR_TXOK | AR_ISR_TXDESC |
190225925Sadrian			    AR_ISR_TXERR | AR_ISR_TXEOL);
191225925Sadrian
192185377Ssam			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK);
193185377Ssam			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC);
194185377Ssam			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR);
195185377Ssam			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL);
196185377Ssam		}
197185377Ssam
198225925Sadrian		if ((isr & AR_ISR_GENTMR) || (! pCap->halAutoSleepSupport)) {
199208711Srpaulo			uint32_t isr5;
200225925Sadrian			isr5 = OS_REG_READ(ah, AR_ISR_S5);
201225925Sadrian			OS_REG_WRITE(ah, AR_ISR_S5, isr5);
202225925Sadrian			isr &= ~AR_ISR_GENTMR;
203225925Sadrian
204225925Sadrian			if (! pCap->halAutoSleepSupport)
205225925Sadrian				if (isr5 & AR_ISR_S5_TIM_TIMER)
206225925Sadrian					*masked |= HAL_INT_TIM_TIMER;
207208711Srpaulo		}
208185377Ssam		*masked |= mask2;
209185377Ssam	}
210221163Sadrian
211225925Sadrian	/*
212225925Sadrian	 * Since we're not using AR_ISR_RAC, clear the status bits
213225925Sadrian	 * for handled interrupts here. For bits whose interrupt
214225925Sadrian	 * source is a secondary register, those bits should've been
215225925Sadrian	 * masked out - instead of those bits being written back,
216225925Sadrian	 * their source (ie, the secondary status registers) should
217225925Sadrian	 * be cleared. That way there are no race conditions with
218225925Sadrian	 * new triggers coming in whilst they've been read/cleared.
219225925Sadrian	 */
220225925Sadrian	OS_REG_WRITE(ah, AR_ISR, isr);
221225925Sadrian	/* Flush previous write */
222225925Sadrian	OS_REG_READ(ah, AR_ISR);
223225925Sadrian
224221163Sadrian	if (AR_SREV_HOWL(ah))
225221163Sadrian		return AH_TRUE;
226221163Sadrian
227185377Ssam	if (sync_cause != 0) {
228233900Sadrian		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: sync_cause=0x%x\n",
229233900Sadrian		    __func__,
230233900Sadrian		    o_sync_cause);
231185377Ssam		if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) {
232185377Ssam			*masked |= HAL_INT_FATAL;
233185377Ssam		}
234185377Ssam		if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
235185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RADM CPL timeout\n",
236185377Ssam			    __func__);
237185377Ssam			OS_REG_WRITE(ah, AR_RC, AR_RC_HOSTIF);
238185377Ssam			OS_REG_WRITE(ah, AR_RC, 0);
239185377Ssam			*masked |= HAL_INT_FATAL;
240185377Ssam		}
241185377Ssam		/*
242185377Ssam		 * On fatal errors collect ISR state for debugging.
243185377Ssam		 */
244185377Ssam		if (*masked & HAL_INT_FATAL) {
245185377Ssam			AH_PRIVATE(ah)->ah_fatalState[0] = isr;
246185377Ssam			AH_PRIVATE(ah)->ah_fatalState[1] = sync_cause;
247185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
248185377Ssam			    "%s: fatal error, ISR_RAC 0x%x SYNC_CAUSE 0x%x\n",
249185377Ssam			    __func__, isr, sync_cause);
250185377Ssam		}
251185377Ssam
252185377Ssam		OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
253185377Ssam		/* NB: flush write */
254185377Ssam		(void) OS_REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR);
255185377Ssam	}
256185377Ssam	return AH_TRUE;
257185377Ssam}
258185377Ssam
259185377Ssam/*
260185377Ssam * Atomically enables NIC interrupts.  Interrupts are passed in
261185377Ssam * via the enumerated bitmask in ints.
262185377Ssam */
263185377SsamHAL_INT
264185377Ssamar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints)
265185377Ssam{
266185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
267185377Ssam	uint32_t omask = ahp->ah_maskReg;
268188976Ssam	uint32_t mask, mask2;
269185377Ssam
270185377Ssam	HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: 0x%x => 0x%x\n",
271185377Ssam	    __func__, omask, ints);
272185377Ssam
273185377Ssam	if (omask & HAL_INT_GLOBAL) {
274185377Ssam		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: disable IER\n", __func__);
275185377Ssam		OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
276185377Ssam		(void) OS_REG_READ(ah, AR_IER);
277185377Ssam
278221163Sadrian		if (! AR_SREV_HOWL(ah)) {
279221163Sadrian			OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
280221163Sadrian			(void) OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE);
281185377Ssam
282221163Sadrian			OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
283221163Sadrian			(void) OS_REG_READ(ah, AR_INTR_SYNC_ENABLE);
284221163Sadrian		}
285185377Ssam	}
286185377Ssam
287185377Ssam	mask = ints & HAL_INT_COMMON;
288185377Ssam	mask2 = 0;
289185377Ssam
290220188Sadrian#ifdef	AH_AR5416_INTERRUPT_MITIGATION
291219975Sadrian	/*
292219975Sadrian	 * Overwrite default mask if Interrupt mitigation
293219975Sadrian	 * is specified for AR5416
294219975Sadrian	 */
295219975Sadrian	if (ints & HAL_INT_RX)
296219975Sadrian		mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM;
297219975Sadrian#else
298225921Sadrian	if (ints & HAL_INT_RX)
299225921Sadrian		mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC;
300225921Sadrian#endif
301219975Sadrian	if (ints & HAL_INT_TX) {
302185377Ssam		if (ahp->ah_txOkInterruptMask)
303185377Ssam			mask |= AR_IMR_TXOK;
304185377Ssam		if (ahp->ah_txErrInterruptMask)
305185377Ssam			mask |= AR_IMR_TXERR;
306185377Ssam		if (ahp->ah_txDescInterruptMask)
307185377Ssam			mask |= AR_IMR_TXDESC;
308185377Ssam		if (ahp->ah_txEolInterruptMask)
309185377Ssam			mask |= AR_IMR_TXEOL;
310247030Sadrian		if (ahp->ah_txUrnInterruptMask)
311247030Sadrian			mask |= AR_IMR_TXURN;
312185377Ssam	}
313185377Ssam	if (ints & (HAL_INT_BMISC)) {
314185377Ssam		mask |= AR_IMR_BCNMISC;
315185377Ssam		if (ints & HAL_INT_TIM)
316185377Ssam			mask2 |= AR_IMR_S2_TIM;
317185377Ssam		if (ints & HAL_INT_DTIM)
318185377Ssam			mask2 |= AR_IMR_S2_DTIM;
319185377Ssam		if (ints & HAL_INT_DTIMSYNC)
320185377Ssam			mask2 |= AR_IMR_S2_DTIMSYNC;
321185377Ssam		if (ints & HAL_INT_CABEND)
322185377Ssam			mask2 |= (AR_IMR_S2_CABEND );
323185377Ssam		if (ints & HAL_INT_CST)
324185377Ssam			mask2 |= AR_IMR_S2_CST;
325185377Ssam		if (ints & HAL_INT_TSFOOR)
326185377Ssam			mask2 |= AR_IMR_S2_TSFOOR;
327185377Ssam	}
328185377Ssam
329220779Sadrian	if (ints & (HAL_INT_GTT | HAL_INT_CST)) {
330220779Sadrian		mask |= AR_IMR_BCNMISC;
331220779Sadrian		if (ints & HAL_INT_GTT)
332220779Sadrian			mask2 |= AR_IMR_S2_GTT;
333220779Sadrian		if (ints & HAL_INT_CST)
334220779Sadrian			mask2 |= AR_IMR_S2_CST;
335220779Sadrian	}
336220779Sadrian
337185377Ssam	/* Write the new IMR and store off our SW copy. */
338185377Ssam	HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask);
339185377Ssam	OS_REG_WRITE(ah, AR_IMR, mask);
340185377Ssam	mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
341185377Ssam					AR_IMR_S2_DTIM |
342185377Ssam					AR_IMR_S2_DTIMSYNC |
343185377Ssam					AR_IMR_S2_CABEND |
344185377Ssam					AR_IMR_S2_CABTO  |
345185377Ssam					AR_IMR_S2_TSFOOR |
346185377Ssam					AR_IMR_S2_GTT |
347185377Ssam					AR_IMR_S2_CST);
348185377Ssam	OS_REG_WRITE(ah, AR_IMR_S2, mask | mask2);
349185377Ssam
350185377Ssam	ahp->ah_maskReg = ints;
351185377Ssam
352185377Ssam	/* Re-enable interrupts if they were enabled before. */
353185377Ssam	if (ints & HAL_INT_GLOBAL) {
354185377Ssam		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: enable IER\n", __func__);
355185377Ssam		OS_REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
356185377Ssam
357221163Sadrian		if (! AR_SREV_HOWL(ah)) {
358221163Sadrian			mask = AR_INTR_MAC_IRQ;
359221163Sadrian			if (ints & HAL_INT_GPIO)
360221163Sadrian				mask |= SM(AH5416(ah)->ah_gpioMask,
361221163Sadrian				    AR_INTR_ASYNC_MASK_GPIO);
362221163Sadrian			OS_REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, mask);
363221163Sadrian			OS_REG_WRITE(ah, AR_INTR_ASYNC_MASK, mask);
364185377Ssam
365221163Sadrian			mask = AR_INTR_SYNC_DEFAULT;
366221163Sadrian			if (ints & HAL_INT_GPIO)
367221163Sadrian				mask |= SM(AH5416(ah)->ah_gpioMask,
368221163Sadrian				    AR_INTR_SYNC_MASK_GPIO);
369221163Sadrian			OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, mask);
370221163Sadrian			OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, mask);
371221163Sadrian		}
372185377Ssam	}
373185377Ssam
374185377Ssam	return omask;
375185377Ssam}
376