/** * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level * * @ah: atheros hardware struct * @bIncTrigLevel: whether or not the frame trigger level should be updated * * The frame trigger level specifies the minimum number of bytes, * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO * before the PCU will initiate sending the frame on the air. This can * mean we initiate transmit before a full frame is on the PCU TX FIFO. * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs * first) * * Caution must be taken to ensure to set the frame trigger level based * on the DMA request size. For example if the DMA request size is set to * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because * there need to be enough space in the tx FIFO for the requested transfer * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set * the threshold to a value beyond 6, then the transmit will hang. * * Current dual stream devices have a PCU TX FIFO size of 8 KB. * Current single stream devices have a PCU TX FIFO size of 4 KB, however, * there is a hardware issue which forces us to use 2 KB instead so the * frame trigger level must not exceed 2 KB for these chipsets. */ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) { u32 txcfg, curLevel, newLevel; if (ah->tx_trig_level >= ah->config.max_txtrig_level) return false; ath9k_hw_disable_interrupts(ah); txcfg = REG_READ(ah, AR_TXCFG); curLevel = MS(txcfg, AR_FTRIG); newLevel = curLevel; if (bIncTrigLevel) { if (curLevel < ah->config.max_txtrig_level) newLevel++; } else if (curLevel > MIN_TX_FIFO_THRESHOLD) newLevel--; if (newLevel != curLevel) REG_WRITE(ah, AR_TXCFG, (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG)); ath9k_hw_enable_interrupts(ah); ah->tx_trig_level = newLevel; return newLevel != curLevel; }
static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, u32 intval) { struct ath_hw *ah = sc->sc_ah; ath9k_hw_disable_interrupts(ah); ath9k_hw_reset_tsf(ah); ath9k_beaconq_config(sc); ath9k_hw_beaconinit(ah, nexttbtt, intval); sc->beacon.bmisscnt = 0; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); }
static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; ath9k_hw_gen_timer_stop(ah, timer); /* if no timer is enabled, turn off interrupt mask */ if (timer_table->timer_mask.val == 0) { ath9k_hw_disable_interrupts(ah); ah->imask &= ~ATH9K_INT_GENTIMER; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } }
static void ath9k_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, u32 trig_timeout, u32 timer_period) { ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period); if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { ath9k_hw_disable_interrupts(ah); ah->imask |= ATH9K_INT_GENTIMER; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); } }
static void ath9k_tx99_stop(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); ath_drain_all_txq(sc); ath_startrecv(sc); ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); ieee80211_wake_queues(sc->hw); kfree_skb(sc->tx99_skb); sc->tx99_skb = NULL; sc->tx99_state = false; ath9k_hw_tx99_stop(sc->sc_ah); ath_dbg(common, XMIT, "TX99 stopped\n"); }
irqreturn_t ath_isr(int irq, void *dev) { #define SCHED_INTR ( \ ATH9K_INT_FATAL | \ ATH9K_INT_BB_WATCHDOG | \ ATH9K_INT_RXORN | \ ATH9K_INT_RXEOL | \ ATH9K_INT_RX | \ ATH9K_INT_RXLP | \ ATH9K_INT_RXHP | \ ATH9K_INT_TX | \ ATH9K_INT_BMISS | \ ATH9K_INT_CST | \ ATH9K_INT_TSFOOR | \ ATH9K_INT_GENTIMER) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; bool sched = false; /* * The hardware is not ready/present, don't * touch anything. Note this can happen early * on if the IRQ is shared. */ if (sc->sc_flags & SC_OP_INVALID) return IRQ_NONE; /* shared irq, not for us */ if (!ath9k_hw_intrpend(ah)) return IRQ_NONE; /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ status &= ah->imask; /* discard unasked-for bits */ /* * If there are no status bits set, then this interrupt was not * for me (should have been caught above). */ if (!status) return IRQ_NONE; /* Cache the status */ sc->intrstatus = status; if (status & SCHED_INTR) sched = true; /* * If a FATAL or RXORN interrupt is received, we have to reset the * chip immediately. */ if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) && !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA))) goto chip_reset; if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_BB_WATCHDOG)) { spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); ar9003_hw_bb_watchdog_dbg_info(ah); spin_unlock(&common->cc_lock); goto chip_reset; } if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); if (status & ATH9K_INT_TXURN) ath9k_hw_updatetxtriglevel(ah, true); if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { if (status & ATH9K_INT_RXEOL) { ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah, ah->imask); } } if (status & ATH9K_INT_MIB) { /* * Disable interrupts until we service the MIB * interrupt; otherwise it will continue to * fire. */ ath9k_hw_disable_interrupts(ah); /* * Let the hal handle the event. We assume * it will clear whatever condition caused * the interrupt. */ spin_lock(&common->cc_lock); ath9k_hw_proc_mib_event(ah); spin_unlock(&common->cc_lock); ath9k_hw_enable_interrupts(ah); } if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) if (status & ATH9K_INT_TIM_TIMER) { if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle)) goto chip_reset; /* Clear RxAbort bit so that we can * receive frames */ ath9k_setpower(sc, ATH9K_PM_AWAKE); ath9k_hw_setrxabort(sc->sc_ah, 0); sc->ps_flags |= PS_WAIT_FOR_BEACON; } chip_reset: ath_debug_stat_interrupt(sc, status); if (sched) { /* turn off every interrupt */ ath9k_hw_disable_interrupts(ah); tasklet_schedule(&sc->intr_tq); } return IRQ_HANDLED; #undef SCHED_INTR }
void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); u32 status = sc->intrstatus; u32 rxmask; if ((status & ATH9K_INT_FATAL) || (status & ATH9K_INT_BB_WATCHDOG)) { spin_lock(&sc->sc_pcu_lock); ath_reset(sc, true); spin_unlock(&sc->sc_pcu_lock); return; } ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); /* * Only run the baseband hang check if beacons stop working in AP or * IBSS mode, because it has a high false positive rate. For station * mode it should not be necessary, since the upper layers will detect * this through a beacon miss automatically and the following channel * change will trigger a hardware reset anyway */ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0 && !ath9k_hw_check_alive(ah)) ieee80211_queue_work(sc->hw, &sc->hw_check_work); if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with * the next Beacon. */ ath_dbg(common, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC | PS_TSFOOR_SYNC; } if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); else rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); if (status & rxmask) { /* Check for high priority Rx first */ if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && (status & ATH9K_INT_RXHP)) ath_rx_tasklet(sc, 0, true); ath_rx_tasklet(sc, 0, false); } if (status & ATH9K_INT_TX) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_tx_edma_tasklet(sc); else ath_tx_tasklet(sc); } if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) if (status & ATH9K_INT_GENTIMER) ath_gen_timer_isr(sc->sc_ah); /* re-enable hardware interrupt */ ath9k_hw_enable_interrupts(ah); spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); }
void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) { enum ath9k_int omask = ah->imask; u32 mask, mask2; struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); if (!(ints & ATH9K_INT_GLOBAL)) ath9k_hw_disable_interrupts(ah); ath_dbg(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints); /* TODO: global int Ref count */ mask = ints & ATH9K_INT_COMMON; mask2 = 0; if (ints & ATH9K_INT_TX) { if (ah->config.tx_intr_mitigation) mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM; else { if (ah->txok_interrupt_mask) mask |= AR_IMR_TXOK; if (ah->txdesc_interrupt_mask) mask |= AR_IMR_TXDESC; } if (ah->txerr_interrupt_mask) mask |= AR_IMR_TXERR; if (ah->txeol_interrupt_mask) mask |= AR_IMR_TXEOL; } if (ints & ATH9K_INT_RX) { if (AR_SREV_9300_20_OR_LATER(ah)) { mask |= AR_IMR_RXERR | AR_IMR_RXOK_HP; if (ah->config.rx_intr_mitigation) { mask &= ~AR_IMR_RXOK_LP; mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; } else { mask |= AR_IMR_RXOK_LP; } } else { if (ah->config.rx_intr_mitigation) mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; else mask |= AR_IMR_RXOK | AR_IMR_RXDESC; } if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) mask |= AR_IMR_GENTMR; } if (ints & ATH9K_INT_GENTIMER) mask |= AR_IMR_GENTMR; if (ints & (ATH9K_INT_BMISC)) { mask |= AR_IMR_BCNMISC; if (ints & ATH9K_INT_TIM) mask2 |= AR_IMR_S2_TIM; if (ints & ATH9K_INT_DTIM) mask2 |= AR_IMR_S2_DTIM; if (ints & ATH9K_INT_DTIMSYNC) mask2 |= AR_IMR_S2_DTIMSYNC; if (ints & ATH9K_INT_CABEND) mask2 |= AR_IMR_S2_CABEND; if (ints & ATH9K_INT_TSFOOR) mask2 |= AR_IMR_S2_TSFOOR; } if (ints & (ATH9K_INT_GTT | ATH9K_INT_CST)) { mask |= AR_IMR_BCNMISC; if (ints & ATH9K_INT_GTT) mask2 |= AR_IMR_S2_GTT; if (ints & ATH9K_INT_CST) mask2 |= AR_IMR_S2_CST; } ath_dbg(common, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask); REG_WRITE(ah, AR_IMR, mask); ah->imrs2_reg &= ~(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); ah->imrs2_reg |= mask2; REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if (ints & ATH9K_INT_TIM_TIMER) REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); else REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); } if (ints & ATH9K_INT_GLOBAL) ath9k_hw_enable_interrupts(ah); return; }
/* * This sets up the beacon timers according to the timestamp of the last * received beacon and the current TSF, configures PCF and DTIM * handling, programs the sleep registers so the hardware will wakeup in * time to receive beacons, and configures the beacon miss handling so * we'll receive a BMISS interrupt when we stop seeing beacons from the AP * we've associated with. */ static void ath9k_beacon_config_sta(struct ath_softc *sc, struct ath_beacon_config *conf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_beacon_state bs; int dtimperiod, dtimcount, sleepduration; int cfpperiod, cfpcount; u32 nexttbtt = 0, intval, tsftu; u64 tsf; int num_beacons, offset, dtim_dec_count, cfp_dec_count; /* No need to configure beacon if we are not associated */ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { ath_dbg(common, BEACON, "STA is not yet associated..skipping beacon config\n"); return; } memset(&bs, 0, sizeof(bs)); intval = conf->beacon_interval; /* * Setup dtim and cfp parameters according to * last beacon we received (which may be none). */ dtimperiod = conf->dtim_period; dtimcount = conf->dtim_count; if (dtimcount >= dtimperiod) /* NB: sanity check */ dtimcount = 0; cfpperiod = 1; /* NB: no PCF support yet */ cfpcount = 0; sleepduration = conf->listen_interval * intval; /* * Pull nexttbtt forward to reflect the current * TSF and calculate dtim+cfp state for the result. */ tsf = ath9k_hw_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; num_beacons = tsftu / intval + 1; offset = tsftu % intval; nexttbtt = tsftu - offset; if (offset) nexttbtt += intval; /* DTIM Beacon every dtimperiod Beacon */ dtim_dec_count = num_beacons % dtimperiod; /* CFP every cfpperiod DTIM Beacon */ cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; if (dtim_dec_count) cfp_dec_count++; dtimcount -= dtim_dec_count; if (dtimcount < 0) dtimcount += dtimperiod; cfpcount -= cfp_dec_count; if (cfpcount < 0) cfpcount += cfpperiod; bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; bs.bs_dtimperiod = dtimperiod*intval; bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; bs.bs_cfpmaxduration = 0; /* * Calculate the number of consecutive beacons to miss* before taking * a BMISS interrupt. The configuration is specified in TU so we only * need calculate based on the beacon interval. Note that we clamp the * result to at most 15 beacons. */ if (sleepduration > intval) { bs.bs_bmissthreshold = conf->listen_interval * ATH_DEFAULT_BMISS_LIMIT / 2; } else { bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); if (bs.bs_bmissthreshold > 15) bs.bs_bmissthreshold = 15; else if (bs.bs_bmissthreshold <= 0) bs.bs_bmissthreshold = 1; } /* * Calculate sleep duration. The configuration is given in ms. * We ensure a multiple of the beacon period is used. Also, if the sleep * duration is greater than the DTIM period then it makes senses * to make it a multiple of that. * * XXX fixed at 100ms */ bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = bs.bs_dtimperiod; /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); ath_dbg(common, BEACON, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); /* Set the computed STA beacon timers */ ath9k_hw_disable_interrupts(ah); ath9k_hw_set_sta_beacon_timers(ah, &bs); ah->imask |= ATH9K_INT_BMISS; ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); }