summaryrefslogtreecommitdiff
path: root/hal/ar9300/ar9300_interrupts.c
diff options
context:
space:
mode:
Diffstat (limited to 'hal/ar9300/ar9300_interrupts.c')
-rw-r--r--hal/ar9300/ar9300_interrupts.c767
1 files changed, 767 insertions, 0 deletions
diff --git a/hal/ar9300/ar9300_interrupts.c b/hal/ar9300/ar9300_interrupts.c
new file mode 100644
index 000000000000..cbd83c679773
--- /dev/null
+++ b/hal/ar9300/ar9300_interrupts.c
@@ -0,0 +1,767 @@
+/*
+ * 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"
+
+#ifdef AH_SUPPORT_AR9300
+
+#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;
+ }
+
+ 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->hal_isr_rac_support) {
+ /*
+ * 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->hal_isr_rac_support) {
+ 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->hal_isr_rac_support) {
+ 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->hal_auto_sleep_support) {
+ 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->hal_isr_rac_support) {
+ /* 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->hal_isr_rac_support) {
+ /*
+ * 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->hal_isr_rac_support) {
+ /*
+ * 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->hal_mci_support)
+ {
+ 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_PRIVATE(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 (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
+ if (OS_ATOMIC_READ(&ahp->ah_ier_ref_count) > 0) {
+ OS_ATOMIC_DEC(&ahp->ah_ier_ref_count);
+ }
+ } 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->hal_auto_sleep_support) {
+ 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->hal_auto_sleep_support) {
+ 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_PRIVATE(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;
+}
+
+#endif /* AH_SUPPORT_AR9300 */
+