Date: Wed, 14 Sep 2011 07:56:25 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r225548 - in user/adrian/if_ath_tx/sys/dev/ath/ath_hal: . ar5416 Message-ID: <201109140756.p8E7uP9Y016924@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: adrian Date: Wed Sep 14 07:56:25 2011 New Revision: 225548 URL: http://svn.freebsd.org/changeset/base/225548 Log: Add support to use the non read-and-copy interrupt register path. There appears to be a subtle race condition in the AR_ISR_RAC path where some of the secondary conditions would be not show up. This unfortunately includes TX events such as EOL. The reference driver includes an alternate path which instead uses the AR_ISR and AR_ISR_S{0,1,2,3,4,5} registers instead of AR_ISR_RAC and the shadow AR_ISR_S{0,1,2,3,4,5}_S registers. Here, the interrupts being handled are written back to the status register, clearing them. For interrupts caused by secondary registers, clear those bits instead of the relevant bit in AR_ISR. That way if an event occurs between the ISR_ISR_Sx read and write (clear), it won't be cleared; and it'll trigger another interrupt. This won't _entirely_ limit the TX hangs because of the existing txqactive() race going on between the interrupt handler and the TX process. I'll address that in a future commit. Note: I should also do this for the AR5212 series NICs as well as some of them may suffer from the same race. Finally - for now, never set the relevant HAL capability that I've introduced; I'll set it when I know which chips work and which don't. Finally finally - ath9k doesn't do this for the pre-ar9003 NICs but does for AR9300 v2.0 and later. ie, the AR9300 NIC interrupt code has this RAC capability check, and only enables it for AR9300 v2.0 MAC versions. Obtained from: Atheros, Linux ath9k Modified: user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ah_internal.h user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416reg.h Modified: user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ah_internal.h ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ah_internal.h Wed Sep 14 04:13:48 2011 (r225547) +++ user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ah_internal.h Wed Sep 14 07:56:25 2011 (r225548) @@ -208,7 +208,13 @@ typedef struct { halBssidMatchSupport : 1, hal4kbSplitTransSupport : 1, halHasRxSelfLinkedTail : 1, - halSupportsFastClock5GHz : 1; /* Hardware supports 5ghz fast clock; check eeprom/channel before using */ + /* + * Hardware supports 5ghz fast clock; + * check eeprom/channel before using + */ + halSupportsFastClock5GHz : 1, + /* use AR_ISR_RAC and shadow registers */ + halUseIsrRac : 1; uint32_t halWirelessModes; uint16_t halTotalQueues; uint16_t halKeyCacheSize; Modified: user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c Wed Sep 14 04:13:48 2011 (r225547) +++ user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c Wed Sep 14 07:56:25 2011 (r225548) @@ -68,6 +68,7 @@ HAL_BOOL ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked) { uint32_t isr, isr0, isr1, sync_cause = 0; + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; /* * Verify there's a mac interrupt and the RTC is on. @@ -110,9 +111,22 @@ ar5416GetPendingInterrupts(struct ath_ha mask2 |= HAL_INT_CST; if (isr2 & AR_ISR_S2_TSFOOR) mask2 |= HAL_INT_TSFOOR; + + /* XXX TXURN? */ + + /* + * Don't mask out AR_BCNMISC; instead mask + * out what causes it. + */ + if (! pCap->halUseIsrRac) { + OS_REG_WRITE(ah, AR_ISR_S2, isr2); + isr &= ~AR_ISR_BCNMISC; + } } - isr = OS_REG_READ(ah, AR_ISR_RAC); + if (pCap->halUseIsrRac) + isr = OS_REG_READ(ah, AR_ISR_RAC); + if (isr == 0xffffffff) { *masked = 0; return AH_FALSE; @@ -130,28 +144,64 @@ ar5416GetPendingInterrupts(struct ath_ha */ *masked = isr & HAL_INT_COMMON; - if (isr & (AR_ISR_RXOK | AR_ISR_RXERR | AR_ISR_RXMINTR | AR_ISR_RXINTM)) + if (isr & (AR_ISR_RXOK | AR_ISR_RXERR | AR_ISR_RXMINTR | + AR_ISR_RXINTM)) *masked |= HAL_INT_RX; - if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL | AR_ISR_TXMINTR | AR_ISR_TXINTM)) { + if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | + AR_ISR_TXEOL | AR_ISR_TXMINTR | AR_ISR_TXINTM)) { *masked |= HAL_INT_TX; - isr0 = OS_REG_READ(ah, AR_ISR_S0_S); + if (pCap->halUseIsrRac) { + isr0 = OS_REG_READ(ah, AR_ISR_S0_S); + isr1 = OS_REG_READ(ah, AR_ISR_S1_S); + } else { + isr0 = OS_REG_READ(ah, AR_ISR_S0); + OS_REG_WRITE(ah, AR_ISR_S0, isr0); + isr1 = OS_REG_READ(ah, AR_ISR_S1); + OS_REG_WRITE(ah, AR_ISR_S1, isr1); + + /* + * Don't clear the primary ISR TX bits, clear + * what causes them (S0/S1.) + */ + isr &= ~(AR_ISR_TXOK | AR_ISR_TXDESC | + AR_ISR_TXERR | AR_ISR_TXEOL); + } ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK); ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC); - isr1 = OS_REG_READ(ah, AR_ISR_S1_S); ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR); ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL); } if (AR_SREV_MERLIN(ah) || AR_SREV_KITE(ah)) { uint32_t isr5; - isr5 = OS_REG_READ(ah, AR_ISR_S5_S); + if (pCap->halUseIsrRac) { + isr5 = OS_REG_READ(ah, AR_ISR_S5_S); + } else { + isr5 = OS_REG_READ(ah, AR_ISR_S5); + OS_REG_WRITE(ah, AR_ISR_S5, isr5); + isr &= ~AR_ISR_GENTMR; + } if (isr5 & AR_ISR_S5_TIM_TIMER) *masked |= HAL_INT_TIM_TIMER; } - *masked |= mask2; } + if (! pCap->halUseIsrRac) { + /* + * If we're not using AR_ISR_RAC, clear the status bits + * for handled interrupts here. For bits whose interrupt + * source is a secondary register, those bits should've been + * masked out - instead of those bits being written back, + * their source (ie, the secondary status registers) should + * be cleared. That way there are no race conditions with + * new triggers coming in whilst they've been read/cleared. + */ + OS_REG_WRITE(ah, AR_ISR, isr); + /* Flush previous write */ + OS_REG_READ(ah, AR_ISR); + } + if (AR_SREV_HOWL(ah)) return AH_TRUE; Modified: user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416reg.h ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416reg.h Wed Sep 14 04:13:48 2011 (r225547) +++ user/adrian/if_ath_tx/sys/dev/ath/ath_hal/ar5416/ar5416reg.h Wed Sep 14 07:56:25 2011 (r225548) @@ -242,6 +242,7 @@ /* Interrupts */ #define AR_ISR_TXMINTR 0x00080000 /* Maximum interrupt tx rate */ #define AR_ISR_RXMINTR 0x01000000 /* Maximum interrupt rx rate */ +#define AR_ISR_GENTMR 0x10000000 /* OR of generic timer bits in S5 */ #define AR_ISR_TXINTM 0x40000000 /* Tx int after mitigation */ #define AR_ISR_RXINTM 0x80000000 /* Rx int after mitigation */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201109140756.p8E7uP9Y016924>