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