diff options
Diffstat (limited to 'sys/contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c')
-rw-r--r-- | sys/contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c | 773 |
1 files changed, 773 insertions, 0 deletions
diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c new file mode 100644 index 0000000..51ee3cd --- /dev/null +++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_interrupts.c @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" + +#include "ar9300/ar9300.h" +#include "ar9300/ar9300reg.h" +#include "ar9300/ar9300phy.h" + +/* + * Checks to see if an interrupt is pending on our NIC + * + * Returns: TRUE if an interrupt is pending + * FALSE if not + */ +HAL_BOOL +ar9300_is_interrupt_pending(struct ath_hal *ah) +{ + u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; + u_int32_t host_isr; + + /* + * Some platforms trigger our ISR before applying power to + * the card, so make sure. + */ + host_isr = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE)); + if ((host_isr & AR_INTR_ASYNC_USED) && (host_isr != AR_INTR_SPURIOUS)) { + return AH_TRUE; + } + + host_isr = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE)); + if (AR_SREV_POSEIDON(ah)) { + sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; + } + else if (AR_SREV_WASP(ah)) { + sync_en_def = AR9340_INTR_SYNC_DEFAULT; + } + + if ((host_isr & (sync_en_def | AR_INTR_SYNC_MASK_GPIO)) && + (host_isr != AR_INTR_SPURIOUS)) { + return AH_TRUE; + } + + return AH_FALSE; +} + +/* + * Reads the Interrupt Status Register value from the NIC, thus deasserting + * the interrupt line, and returns both the masked and unmasked mapped ISR + * values. The value returned is mapped to abstract the hw-specific bit + * locations in the Interrupt Status Register. + * + * Returns: A hardware-abstracted bitmap of all non-masked-out + * interrupts pending, as well as an unmasked value + */ +#define MAP_ISR_S2_HAL_CST 6 /* Carrier sense timeout */ +#define MAP_ISR_S2_HAL_GTT 6 /* Global transmit timeout */ +#define MAP_ISR_S2_HAL_TIM 3 /* TIM */ +#define MAP_ISR_S2_HAL_CABEND 0 /* CABEND */ +#define MAP_ISR_S2_HAL_DTIMSYNC 7 /* DTIMSYNC */ +#define MAP_ISR_S2_HAL_DTIM 7 /* DTIM */ +#define MAP_ISR_S2_HAL_TSFOOR 4 /* Rx TSF out of range */ +#define MAP_ISR_S2_HAL_BBPANIC 6 /* Panic watchdog IRQ from BB */ +HAL_BOOL +ar9300_get_pending_interrupts( + struct ath_hal *ah, + HAL_INT *masked, + HAL_INT_TYPE type, + u_int8_t msi, + HAL_BOOL nortc) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_BOOL ret_val = AH_TRUE; + u_int32_t isr = 0; + u_int32_t mask2 = 0; + u_int32_t sync_cause = 0; + u_int32_t async_cause; + u_int32_t msi_pend_addr_mask = 0; + u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; + HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; + + *masked = 0; + + if (!nortc) { + if (HAL_INT_MSI == type) { + if (msi == HAL_MSIVEC_RXHP) { + OS_REG_WRITE(ah, AR_ISR, AR_ISR_HP_RXOK); + *masked = HAL_INT_RXHP; + goto end; + } else if (msi == HAL_MSIVEC_RXLP) { + OS_REG_WRITE(ah, AR_ISR, + (AR_ISR_LP_RXOK | AR_ISR_RXMINTR | AR_ISR_RXINTM)); + *masked = HAL_INT_RXLP; + goto end; + } else if (msi == HAL_MSIVEC_TX) { + OS_REG_WRITE(ah, AR_ISR, AR_ISR_TXOK); + *masked = HAL_INT_TX; + goto end; + } else if (msi == HAL_MSIVEC_MISC) { + /* + * For the misc MSI event fall through and determine the cause. + */ + } + } + } + + /* Make sure mac interrupt is pending in async interrupt cause register */ + async_cause = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE)); + if (async_cause & AR_INTR_ASYNC_USED) { + /* + * RTC may not be on since it runs on a slow 32khz clock + * so check its status to be sure + */ + if (!nortc && + (OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == + AR_RTC_STATUS_ON) + { + isr = OS_REG_READ(ah, AR_ISR); + } + } + + if (AR_SREV_POSEIDON(ah)) { + sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; + } + else if (AR_SREV_WASP(ah)) { + sync_en_def = AR9340_INTR_SYNC_DEFAULT; + } + + sync_cause = + OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE)) & + (sync_en_def | AR_INTR_SYNC_MASK_GPIO); + + if (!isr && !sync_cause && !async_cause) { + ret_val = AH_FALSE; + goto end; + } + + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: isr=0x%x, sync_cause=0x%x, async_cause=0x%x\n", + __func__, + isr, + sync_cause, + async_cause); + + if (isr) { + if (isr & AR_ISR_BCNMISC) { + u_int32_t isr2; + isr2 = OS_REG_READ(ah, AR_ISR_S2); + + /* Translate ISR bits to HAL values */ + mask2 |= ((isr2 & AR_ISR_S2_TIM) >> MAP_ISR_S2_HAL_TIM); + mask2 |= ((isr2 & AR_ISR_S2_DTIM) >> MAP_ISR_S2_HAL_DTIM); + mask2 |= ((isr2 & AR_ISR_S2_DTIMSYNC) >> MAP_ISR_S2_HAL_DTIMSYNC); + mask2 |= ((isr2 & AR_ISR_S2_CABEND) >> MAP_ISR_S2_HAL_CABEND); + mask2 |= ((isr2 & AR_ISR_S2_GTT) << MAP_ISR_S2_HAL_GTT); + mask2 |= ((isr2 & AR_ISR_S2_CST) << MAP_ISR_S2_HAL_CST); + mask2 |= ((isr2 & AR_ISR_S2_TSFOOR) >> MAP_ISR_S2_HAL_TSFOOR); + mask2 |= ((isr2 & AR_ISR_S2_BBPANIC) >> MAP_ISR_S2_HAL_BBPANIC); + + if (!p_cap->halIsrRacSupport) { + /* + * EV61133 (missing interrupts due to ISR_RAC): + * If not using ISR_RAC, clear interrupts by writing to ISR_S2. + * This avoids a race condition where a new BCNMISC interrupt + * could come in between reading the ISR and clearing the + * interrupt via the primary ISR. We therefore clear the + * interrupt via the secondary, which avoids this race. + */ + OS_REG_WRITE(ah, AR_ISR_S2, isr2); + isr &= ~AR_ISR_BCNMISC; + } + } + + /* Use AR_ISR_RAC only if chip supports it. + * See EV61133 (missing interrupts due to ISR_RAC) + */ + if (p_cap->halIsrRacSupport) { + isr = OS_REG_READ(ah, AR_ISR_RAC); + } + if (isr == 0xffffffff) { + *masked = 0; + ret_val = AH_FALSE; + goto end; + } + + *masked = isr & HAL_INT_COMMON; + + /* + * When interrupt mitigation is switched on, we fake a normal RX or TX + * interrupt when we received a mitigated interrupt. This way, the upper + * layer do not need to know about feature. + */ + if (ahp->ah_intr_mitigation_rx) { + /* Only Rx interrupt mitigation. No Tx intr. mitigation. */ + if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) { + *masked |= HAL_INT_RXLP; + } + } + if (ahp->ah_intr_mitigation_tx) { + if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM)) { + *masked |= HAL_INT_TX; + } + } + + if (isr & (AR_ISR_LP_RXOK | AR_ISR_RXERR)) { + *masked |= HAL_INT_RXLP; + } + if (isr & AR_ISR_HP_RXOK) { + *masked |= HAL_INT_RXHP; + } + if (isr & (AR_ISR_TXOK | AR_ISR_TXERR | AR_ISR_TXEOL)) { + *masked |= HAL_INT_TX; + + if (!p_cap->halIsrRacSupport) { + u_int32_t s0, s1; + /* + * EV61133 (missing interrupts due to ISR_RAC): + * If not using ISR_RAC, clear interrupts by writing to + * ISR_S0/S1. + * This avoids a race condition where a new interrupt + * could come in between reading the ISR and clearing the + * interrupt via the primary ISR. We therefore clear the + * interrupt via the secondary, which avoids this race. + */ + s0 = OS_REG_READ(ah, AR_ISR_S0); + OS_REG_WRITE(ah, AR_ISR_S0, s0); + s1 = OS_REG_READ(ah, AR_ISR_S1); + OS_REG_WRITE(ah, AR_ISR_S1, s1); + + isr &= ~(AR_ISR_TXOK | AR_ISR_TXERR | AR_ISR_TXEOL); + } + } + + /* + * Do not treat receive overflows as fatal for owl. + */ + if (isr & AR_ISR_RXORN) { +#if __PKT_SERIOUS_ERRORS__ + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: receive FIFO overrun interrupt\n", __func__); +#endif + } + +#if 0 + /* XXX Verify if this is fixed for Osprey */ + if (!p_cap->halAutoSleepSupport) { + u_int32_t isr5 = OS_REG_READ(ah, AR_ISR_S5_S); + if (isr5 & AR_ISR_S5_TIM_TIMER) { + *masked |= HAL_INT_TIM_TIMER; + } + } +#endif + if (isr & AR_ISR_GENTMR) { + u_int32_t s5; + + if (p_cap->halIsrRacSupport) { + /* Use secondary shadow registers if using ISR_RAC */ + s5 = OS_REG_READ(ah, AR_ISR_S5_S); + } else { + s5 = OS_REG_READ(ah, AR_ISR_S5); + } + if (isr & AR_ISR_GENTMR) { + + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: GENTIMER, ISR_RAC=0x%x ISR_S2_S=0x%x\n", __func__, + isr, s5); + ahp->ah_intr_gen_timer_trigger = + MS(s5, AR_ISR_S5_GENTIMER_TRIG); + ahp->ah_intr_gen_timer_thresh = + MS(s5, AR_ISR_S5_GENTIMER_THRESH); + if (ahp->ah_intr_gen_timer_trigger) { + *masked |= HAL_INT_GENTIMER; + } + } + if (!p_cap->halIsrRacSupport) { + /* + * EV61133 (missing interrupts due to ISR_RAC): + * If not using ISR_RAC, clear interrupts by writing to ISR_S5. + * This avoids a race condition where a new interrupt + * could come in between reading the ISR and clearing the + * interrupt via the primary ISR. We therefore clear the + * interrupt via the secondary, which avoids this race. + */ + OS_REG_WRITE(ah, AR_ISR_S5, s5); + isr &= ~AR_ISR_GENTMR; + } + } + + *masked |= mask2; + + if (!p_cap->halIsrRacSupport) { + /* + * EV61133 (missing interrupts due to ISR_RAC): + * If not using ISR_RAC, clear the interrupts we've read by + * writing back ones in these locations to the primary ISR + * (except for interrupts that have a secondary isr register - + * see above). + */ + OS_REG_WRITE(ah, AR_ISR, isr); + + /* Flush prior write */ + (void) OS_REG_READ(ah, AR_ISR); + } + +#ifdef AH_SUPPORT_AR9300 + if (*masked & HAL_INT_BBPANIC) { + ar9300_handle_bb_panic(ah); + } +#endif + } + + if (async_cause) { + if (nortc) { + OS_REG_WRITE(ah, + AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE_CLR), async_cause); + /* Flush prior write */ + (void) OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE_CLR)); + } else { +#ifdef ATH_GPIO_USE_ASYNC_CAUSE + if (async_cause & AR_INTR_ASYNC_CAUSE_GPIO) { + ahp->ah_gpio_cause = (async_cause & AR_INTR_ASYNC_CAUSE_GPIO) >> + AR_INTR_ASYNC_ENABLE_GPIO_S; + *masked |= HAL_INT_GPIO; + } +#endif + } + +#if ATH_SUPPORT_MCI + if ((async_cause & AR_INTR_ASYNC_CAUSE_MCI) && + p_cap->halMciSupport) + { + u_int32_t int_raw, int_rx_msg; + + int_rx_msg = OS_REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW); + int_raw = OS_REG_READ(ah, AR_MCI_INTERRUPT_RAW); + + if ((int_raw == 0xdeadbeef) || (int_rx_msg == 0xdeadbeef)) + { + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) Get 0xdeadbeef during MCI int processing" + "new int_raw=0x%08x, new rx_msg_raw=0x%08x, " + "int_raw=0x%08x, rx_msg_raw=0x%08x\n", + int_raw, int_rx_msg, ahp->ah_mci_int_raw, + ahp->ah_mci_int_rx_msg); + } + else { + if (ahp->ah_mci_int_raw || ahp->ah_mci_int_rx_msg) { + ahp->ah_mci_int_rx_msg |= int_rx_msg; + ahp->ah_mci_int_raw |= int_raw; + } + else { + ahp->ah_mci_int_rx_msg = int_rx_msg; + ahp->ah_mci_int_raw = int_raw; + } + + *masked |= HAL_INT_MCI; + ahp->ah_mci_rx_status = OS_REG_READ(ah, AR_MCI_RX_STATUS); + if (int_rx_msg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { + ahp->ah_mci_cont_status = + OS_REG_READ(ah, AR_MCI_CONT_STATUS); + } + OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + int_rx_msg); + OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, int_raw); + + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s:AR_INTR_SYNC_MCI\n", __func__); + } + } +#endif + } + + if (sync_cause) { + int host1_fatal, host1_perr, radm_cpl_timeout, local_timeout; + + host1_fatal = AR_SREV_WASP(ah) ? + AR9340_INTR_SYNC_HOST1_FATAL : AR9300_INTR_SYNC_HOST1_FATAL; + host1_perr = AR_SREV_WASP(ah) ? + AR9340_INTR_SYNC_HOST1_PERR : AR9300_INTR_SYNC_HOST1_PERR; + radm_cpl_timeout = AR_SREV_WASP(ah) ? + 0x0 : AR9300_INTR_SYNC_RADM_CPL_TIMEOUT; + local_timeout = AR_SREV_WASP(ah) ? + AR9340_INTR_SYNC_LOCAL_TIMEOUT : AR9300_INTR_SYNC_LOCAL_TIMEOUT; + + if (sync_cause & host1_fatal) { +#if __PKT_SERIOUS_ERRORS__ + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: received PCI FATAL interrupt\n", __func__); +#endif + *masked |= HAL_INT_FATAL; /* Set FATAL INT flag here;*/ + } + if (sync_cause & host1_perr) { +#if __PKT_SERIOUS_ERRORS__ + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: received PCI PERR interrupt\n", __func__); +#endif + } + + if (sync_cause & radm_cpl_timeout) { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: AR_INTR_SYNC_RADM_CPL_TIMEOUT\n", + __func__); + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), 0); + *masked |= HAL_INT_FATAL; + } + if (sync_cause & local_timeout) { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: AR_INTR_SYNC_LOCAL_TIMEOUT\n", + __func__); + } + +#ifndef ATH_GPIO_USE_ASYNC_CAUSE + if (sync_cause & AR_INTR_SYNC_MASK_GPIO) { + ahp->ah_gpio_cause = (sync_cause & AR_INTR_SYNC_MASK_GPIO) >> + AR_INTR_SYNC_ENABLE_GPIO_S; + *masked |= HAL_INT_GPIO; + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: AR_INTR_SYNC_GPIO\n", __func__); + } +#endif + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR), sync_cause); + /* Flush prior write */ + (void) OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR)); + } + +end: + if (HAL_INT_MSI == type) { + /* + * WAR for Bug EV#75887 + * In normal case, SW read HOST_INTF_PCIE_MSI (0x40A4) and write + * into ah_msi_reg. Then use value of ah_msi_reg to set bit#25 + * when want to enable HW write the cfg_msi_pending. + * Sometimes, driver get MSI interrupt before read 0x40a4 and + * ah_msi_reg is initialization value (0x0). + * We don't know why "MSI interrupt earlier than driver read" now... + */ + if (!ahp->ah_msi_reg) { + ahp->ah_msi_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI)); + } + if (AR_SREV_POSEIDON(ah)) { + msi_pend_addr_mask = AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64; + } else { + msi_pend_addr_mask = AR_PCIE_MSI_HW_INT_PENDING_ADDR; + } + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI), + ((ahp->ah_msi_reg | AR_PCIE_MSI_ENABLE) & msi_pend_addr_mask)); + + } + + return ret_val; +} + +HAL_INT +ar9300_get_interrupts(struct ath_hal *ah) +{ + return AH9300(ah)->ah_mask_reg; +} + +/* + * Atomically enables NIC interrupts. Interrupts are passed in + * via the enumerated bitmask in ints. + */ +HAL_INT +ar9300_set_interrupts(struct ath_hal *ah, HAL_INT ints, HAL_BOOL nortc) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t omask = ahp->ah_mask_reg; + u_int32_t mask, mask2, msi_mask = 0; + u_int32_t msi_pend_addr_mask = 0; + u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; + HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; + + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: 0x%x => 0x%x\n", __func__, omask, ints); + + if (omask & HAL_INT_GLOBAL) { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: disable IER\n", __func__); + + if (ah->ah_config.ath_hal_enable_msi) { + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE), 0); + /* flush write to HW */ + (void)OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE)); + } + + if (!nortc) { + OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE); + (void) OS_REG_READ(ah, AR_IER); /* flush write to HW */ + } + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); + /* flush write to HW */ + (void) OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE)); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE), 0); + /* flush write to HW */ + (void) OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE)); + } + + if (!nortc) { + /* reference count for global IER */ + if (ints & HAL_INT_GLOBAL) { +#ifdef AH_DEBUG + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: Request HAL_INT_GLOBAL ENABLED\n", __func__); +#if 0 + if (OS_ATOMIC_READ(&ahp->ah_ier_ref_count) == 0) { + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: WARNING: ah_ier_ref_count is 0 " + "and attempting to enable IER\n", + __func__); + } +#endif +#endif +#if 0 + if (OS_ATOMIC_READ(&ahp->ah_ier_ref_count) > 0) { + OS_ATOMIC_DEC(&ahp->ah_ier_ref_count); + } +#endif + } else { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: Request HAL_INT_GLOBAL DISABLED\n", __func__); + OS_ATOMIC_INC(&ahp->ah_ier_ref_count); + } + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: ah_ier_ref_count = %d\n", __func__, ahp->ah_ier_ref_count); + + mask = ints & HAL_INT_COMMON; + mask2 = 0; + msi_mask = 0; + + if (ints & HAL_INT_TX) { + if (ahp->ah_intr_mitigation_tx) { + mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM; + } else if (ahp->ah_tx_ok_interrupt_mask) { + mask |= AR_IMR_TXOK; + } + msi_mask |= AR_INTR_PRIO_TX; + if (ahp->ah_tx_err_interrupt_mask) { + mask |= AR_IMR_TXERR; + } + if (ahp->ah_tx_eol_interrupt_mask) { + mask |= AR_IMR_TXEOL; + } + } + if (ints & HAL_INT_RX) { + mask |= AR_IMR_RXERR | AR_IMR_RXOK_HP; + if (ahp->ah_intr_mitigation_rx) { + mask &= ~(AR_IMR_RXOK_LP); + mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; + } else { + mask |= AR_IMR_RXOK_LP; + } + msi_mask |= AR_INTR_PRIO_RXLP | AR_INTR_PRIO_RXHP; + if (! p_cap->halAutoSleepSupport) { + mask |= AR_IMR_GENTMR; + } + } + + if (ints & (HAL_INT_BMISC)) { + mask |= AR_IMR_BCNMISC; + if (ints & HAL_INT_TIM) { + mask2 |= AR_IMR_S2_TIM; + } + if (ints & HAL_INT_DTIM) { + mask2 |= AR_IMR_S2_DTIM; + } + if (ints & HAL_INT_DTIMSYNC) { + mask2 |= AR_IMR_S2_DTIMSYNC; + } + if (ints & HAL_INT_CABEND) { + mask2 |= (AR_IMR_S2_CABEND); + } + if (ints & HAL_INT_TSFOOR) { + mask2 |= AR_IMR_S2_TSFOOR; + } + } + + if (ints & (HAL_INT_GTT | HAL_INT_CST)) { + mask |= AR_IMR_BCNMISC; + if (ints & HAL_INT_GTT) { + mask2 |= AR_IMR_S2_GTT; + } + if (ints & HAL_INT_CST) { + mask2 |= AR_IMR_S2_CST; + } + } + + if (ints & HAL_INT_BBPANIC) { + /* EV92527 - MAC secondary interrupt must enable AR_IMR_BCNMISC */ + mask |= AR_IMR_BCNMISC; + mask2 |= AR_IMR_S2_BBPANIC; + } + + if (ints & HAL_INT_GENTIMER) { + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "%s: enabling gen timer\n", __func__); + mask |= AR_IMR_GENTMR; + } + + /* Write the new IMR and store off our SW copy. */ + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask); + OS_REG_WRITE(ah, AR_IMR, mask); + ahp->ah_mask2Reg &= ~(AR_IMR_S2_TIM | + AR_IMR_S2_DTIM | + AR_IMR_S2_DTIMSYNC | + AR_IMR_S2_CABEND | + AR_IMR_S2_CABTO | + AR_IMR_S2_TSFOOR | + AR_IMR_S2_GTT | + AR_IMR_S2_CST | + AR_IMR_S2_BBPANIC); + ahp->ah_mask2Reg |= mask2; + OS_REG_WRITE(ah, AR_IMR_S2, ahp->ah_mask2Reg ); + ahp->ah_mask_reg = ints; + + if (! p_cap->halAutoSleepSupport) { + if (ints & HAL_INT_TIM_TIMER) { + OS_REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + } + else { + OS_REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + } + } + } + + /* Re-enable interrupts if they were enabled before. */ +#if HAL_INTR_REFCOUNT_DISABLE + if ((ints & HAL_INT_GLOBAL)) { +#else + if ((ints & HAL_INT_GLOBAL) && (OS_ATOMIC_READ(&ahp->ah_ier_ref_count) == 0)) { +#endif + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: enable IER\n", __func__); + + if (!nortc) { + OS_REG_WRITE(ah, AR_IER, AR_IER_ENABLE); + } + + mask = AR_INTR_MAC_IRQ; +#ifdef ATH_GPIO_USE_ASYNC_CAUSE + if (ints & HAL_INT_GPIO) { + if (ahp->ah_gpio_mask) { + mask |= SM(ahp->ah_gpio_mask, AR_INTR_ASYNC_MASK_GPIO); + } + } +#endif + +#if ATH_SUPPORT_MCI + if (ints & HAL_INT_MCI) { + mask |= AR_INTR_ASYNC_MASK_MCI; + } +#endif + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE), mask); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_MASK), mask); + + if (ah->ah_config.ath_hal_enable_msi) { + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE), + msi_mask); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK), + msi_mask); + if (AR_SREV_POSEIDON(ah)) { + msi_pend_addr_mask = AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64; + } else { + msi_pend_addr_mask = AR_PCIE_MSI_HW_INT_PENDING_ADDR; + } + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI), + ((ahp->ah_msi_reg | AR_PCIE_MSI_ENABLE) & msi_pend_addr_mask)); + } + + /* + * debug - enable to see all synchronous interrupts status + * Enable synchronous GPIO interrupts as well, since some async + * GPIO interrupts don't wake the chip up. + */ + mask = 0; +#ifndef ATH_GPIO_USE_ASYNC_CAUSE + if (ints & HAL_INT_GPIO) { + mask |= SM(ahp->ah_gpio_mask, AR_INTR_SYNC_MASK_GPIO); + } +#endif + if (AR_SREV_POSEIDON(ah)) { + sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; + } + else if (AR_SREV_WASP(ah)) { + sync_en_def = AR9340_INTR_SYNC_DEFAULT; + } + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), + (sync_en_def | mask)); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK), + (sync_en_def | mask)); + + HALDEBUG(ah, HAL_DEBUG_INTERRUPT, + "AR_IMR 0x%x IER 0x%x\n", + OS_REG_READ(ah, AR_IMR), OS_REG_READ(ah, AR_IER)); + } + + return omask; +} + +void +ar9300_set_intr_mitigation_timer( + struct ath_hal* ah, + HAL_INT_MITIGATION reg, + u_int32_t value) +{ +#ifdef AR5416_INT_MITIGATION + switch (reg) { + case HAL_INT_THRESHOLD: + OS_REG_WRITE(ah, AR_MIRT, 0); + break; + case HAL_INT_RX_LASTPKT: + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, value); + break; + case HAL_INT_RX_FIRSTPKT: + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, value); + break; + case HAL_INT_TX_LASTPKT: + OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, value); + break; + case HAL_INT_TX_FIRSTPKT: + OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, value); + break; + default: + break; + } +#endif +} + +u_int32_t +ar9300_get_intr_mitigation_timer(struct ath_hal* ah, HAL_INT_MITIGATION reg) +{ + u_int32_t val = 0; +#ifdef AR5416_INT_MITIGATION + switch (reg) { + case HAL_INT_THRESHOLD: + val = OS_REG_READ(ah, AR_MIRT); + break; + case HAL_INT_RX_LASTPKT: + val = OS_REG_READ(ah, AR_RIMT) & 0xFFFF; + break; + case HAL_INT_RX_FIRSTPKT: + val = OS_REG_READ(ah, AR_RIMT) >> 16; + break; + case HAL_INT_TX_LASTPKT: + val = OS_REG_READ(ah, AR_TIMT) & 0xFFFF; + break; + case HAL_INT_TX_FIRSTPKT: + val = OS_REG_READ(ah, AR_TIMT) >> 16; + break; + default: + break; + } +#endif + return val; +} |