1ef1b6cd9SSujith Manoharan /* 2ef1b6cd9SSujith Manoharan * Copyright (c) 2012 Qualcomm Atheros, Inc. 3ef1b6cd9SSujith Manoharan * 4ef1b6cd9SSujith Manoharan * Permission to use, copy, modify, and/or distribute this software for any 5ef1b6cd9SSujith Manoharan * purpose with or without fee is hereby granted, provided that the above 6ef1b6cd9SSujith Manoharan * copyright notice and this permission notice appear in all copies. 7ef1b6cd9SSujith Manoharan * 8ef1b6cd9SSujith Manoharan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9ef1b6cd9SSujith Manoharan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10ef1b6cd9SSujith Manoharan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11ef1b6cd9SSujith Manoharan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12ef1b6cd9SSujith Manoharan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13ef1b6cd9SSujith Manoharan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14ef1b6cd9SSujith Manoharan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15ef1b6cd9SSujith Manoharan */ 16ef1b6cd9SSujith Manoharan 17ef1b6cd9SSujith Manoharan #include "ath9k.h" 18ef1b6cd9SSujith Manoharan 19ef1b6cd9SSujith Manoharan /* 20ef1b6cd9SSujith Manoharan * TX polling - checks if the TX engine is stuck somewhere 21ef1b6cd9SSujith Manoharan * and issues a chip reset if so. 22ef1b6cd9SSujith Manoharan */ 23d63ffc45SFelix Fietkau static bool ath_tx_complete_check(struct ath_softc *sc) 24ef1b6cd9SSujith Manoharan { 25ef1b6cd9SSujith Manoharan struct ath_txq *txq; 26ef1b6cd9SSujith Manoharan int i; 27ef1b6cd9SSujith Manoharan 28d63ffc45SFelix Fietkau if (sc->tx99_state) 29d63ffc45SFelix Fietkau return true; 3089f927afSLuis R. Rodriguez 3101d4ab96SFelix Fietkau for (i = 0; i < IEEE80211_NUM_ACS; i++) { 3201d4ab96SFelix Fietkau txq = sc->tx.txq_map[i]; 3301d4ab96SFelix Fietkau 34ef1b6cd9SSujith Manoharan ath_txq_lock(sc, txq); 35ef1b6cd9SSujith Manoharan if (txq->axq_depth) { 36ef1b6cd9SSujith Manoharan if (txq->axq_tx_inprogress) { 37ef1b6cd9SSujith Manoharan ath_txq_unlock(sc, txq); 38d63ffc45SFelix Fietkau goto reset; 39ef1b6cd9SSujith Manoharan } 40d63ffc45SFelix Fietkau 41d63ffc45SFelix Fietkau txq->axq_tx_inprogress = true; 42ef1b6cd9SSujith Manoharan } 43d4c04ba1SSujith Manoharan ath_txq_unlock(sc, txq); 44ef1b6cd9SSujith Manoharan } 45ef1b6cd9SSujith Manoharan 46d63ffc45SFelix Fietkau return true; 47d63ffc45SFelix Fietkau 48d63ffc45SFelix Fietkau reset: 49ef1b6cd9SSujith Manoharan ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, 50ef1b6cd9SSujith Manoharan "tx hung, resetting the chip\n"); 51124b979bSRajkumar Manoharan ath9k_queue_reset(sc, RESET_TYPE_TX_HANG); 52d63ffc45SFelix Fietkau return false; 53d63ffc45SFelix Fietkau 54ef1b6cd9SSujith Manoharan } 55ef1b6cd9SSujith Manoharan 56d63ffc45SFelix Fietkau void ath_hw_check_work(struct work_struct *work) 57d63ffc45SFelix Fietkau { 58d63ffc45SFelix Fietkau struct ath_softc *sc = container_of(work, struct ath_softc, 59d63ffc45SFelix Fietkau hw_check_work.work); 60d63ffc45SFelix Fietkau 61d63ffc45SFelix Fietkau if (!ath_hw_check(sc) || 62d63ffc45SFelix Fietkau !ath_tx_complete_check(sc)) 63d63ffc45SFelix Fietkau return; 64d63ffc45SFelix Fietkau 65d63ffc45SFelix Fietkau ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work, 66d63ffc45SFelix Fietkau msecs_to_jiffies(ATH_HW_CHECK_POLL_INT)); 67ef1b6cd9SSujith Manoharan } 68ef1b6cd9SSujith Manoharan 69ef1b6cd9SSujith Manoharan /* 70ef1b6cd9SSujith Manoharan * Checks if the BB/MAC is hung. 71ef1b6cd9SSujith Manoharan */ 72415ec61bSSujith Manoharan bool ath_hw_check(struct ath_softc *sc) 73ef1b6cd9SSujith Manoharan { 74ef1b6cd9SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 75124b979bSRajkumar Manoharan enum ath_reset_type type; 76415ec61bSSujith Manoharan bool is_alive; 77ef1b6cd9SSujith Manoharan 78ef1b6cd9SSujith Manoharan ath9k_ps_wakeup(sc); 79415ec61bSSujith Manoharan 80ef1b6cd9SSujith Manoharan is_alive = ath9k_hw_check_alive(sc->sc_ah); 81ef1b6cd9SSujith Manoharan 82415ec61bSSujith Manoharan if (!is_alive) { 83ef1b6cd9SSujith Manoharan ath_dbg(common, RESET, 84415ec61bSSujith Manoharan "HW hang detected, schedule chip reset\n"); 85124b979bSRajkumar Manoharan type = RESET_TYPE_MAC_HANG; 86124b979bSRajkumar Manoharan ath9k_queue_reset(sc, type); 87415ec61bSSujith Manoharan } 88415ec61bSSujith Manoharan 89ef1b6cd9SSujith Manoharan ath9k_ps_restore(sc); 90415ec61bSSujith Manoharan 91415ec61bSSujith Manoharan return is_alive; 92ef1b6cd9SSujith Manoharan } 93ef1b6cd9SSujith Manoharan 94ef1b6cd9SSujith Manoharan /* 95af68abadSSujith Manoharan * PLL-WAR for AR9485/AR9340 96ef1b6cd9SSujith Manoharan */ 97af68abadSSujith Manoharan static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) 98ef1b6cd9SSujith Manoharan { 99ef1b6cd9SSujith Manoharan static int count; 100ef1b6cd9SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 101ef1b6cd9SSujith Manoharan 102ef1b6cd9SSujith Manoharan if (pll_sqsum >= 0x40000) { 103ef1b6cd9SSujith Manoharan count++; 104ef1b6cd9SSujith Manoharan if (count == 3) { 105af68abadSSujith Manoharan ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); 106124b979bSRajkumar Manoharan ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG); 107ef1b6cd9SSujith Manoharan count = 0; 108af68abadSSujith Manoharan return true; 109ef1b6cd9SSujith Manoharan } 110af68abadSSujith Manoharan } else { 111ef1b6cd9SSujith Manoharan count = 0; 112ef1b6cd9SSujith Manoharan } 113ef1b6cd9SSujith Manoharan 114af68abadSSujith Manoharan return false; 115af68abadSSujith Manoharan } 116af68abadSSujith Manoharan 117ef1b6cd9SSujith Manoharan void ath_hw_pll_work(struct work_struct *work) 118ef1b6cd9SSujith Manoharan { 119af68abadSSujith Manoharan u32 pll_sqsum; 120ef1b6cd9SSujith Manoharan struct ath_softc *sc = container_of(work, struct ath_softc, 121ef1b6cd9SSujith Manoharan hw_pll_work.work); 122eefa01ddSOleksij Rempel struct ath_common *common = ath9k_hw_common(sc->sc_ah); 12364bc1239SMohammed Shafi Shajakhan /* 12464bc1239SMohammed Shafi Shajakhan * ensure that the PLL WAR is executed only 12564bc1239SMohammed Shafi Shajakhan * after the STA is associated (or) if the 12664bc1239SMohammed Shafi Shajakhan * beaconing had started in interfaces that 12764bc1239SMohammed Shafi Shajakhan * uses beacons. 12864bc1239SMohammed Shafi Shajakhan */ 129eefa01ddSOleksij Rempel if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) 13064bc1239SMohammed Shafi Shajakhan return; 131ef1b6cd9SSujith Manoharan 13289f927afSLuis R. Rodriguez if (sc->tx99_state) 13389f927afSLuis R. Rodriguez return; 13489f927afSLuis R. Rodriguez 135ef1b6cd9SSujith Manoharan ath9k_ps_wakeup(sc); 136ef1b6cd9SSujith Manoharan pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); 137ef1b6cd9SSujith Manoharan ath9k_ps_restore(sc); 138af68abadSSujith Manoharan if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) 139af68abadSSujith Manoharan return; 140af68abadSSujith Manoharan 141af68abadSSujith Manoharan ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, 142af68abadSSujith Manoharan msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); 143ef1b6cd9SSujith Manoharan } 144ef1b6cd9SSujith Manoharan 145ef1b6cd9SSujith Manoharan /* 146ef1b6cd9SSujith Manoharan * PA Pre-distortion. 147ef1b6cd9SSujith Manoharan */ 148ef1b6cd9SSujith Manoharan static void ath_paprd_activate(struct ath_softc *sc) 149ef1b6cd9SSujith Manoharan { 150ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 151914d0f4dSSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 152ef1b6cd9SSujith Manoharan struct ath9k_hw_cal_data *caldata = ah->caldata; 153ef1b6cd9SSujith Manoharan int chain; 154ef1b6cd9SSujith Manoharan 1554b9b42bfSSujith Manoharan if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) { 156914d0f4dSSujith Manoharan ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n"); 157ef1b6cd9SSujith Manoharan return; 158914d0f4dSSujith Manoharan } 159ef1b6cd9SSujith Manoharan 160ef1b6cd9SSujith Manoharan ar9003_paprd_enable(ah, false); 161ef1b6cd9SSujith Manoharan for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { 162ef1b6cd9SSujith Manoharan if (!(ah->txchainmask & BIT(chain))) 163ef1b6cd9SSujith Manoharan continue; 164ef1b6cd9SSujith Manoharan 165ef1b6cd9SSujith Manoharan ar9003_paprd_populate_single_table(ah, caldata, chain); 166ef1b6cd9SSujith Manoharan } 167ef1b6cd9SSujith Manoharan 168914d0f4dSSujith Manoharan ath_dbg(common, CALIBRATE, "Activating PAPRD\n"); 169ef1b6cd9SSujith Manoharan ar9003_paprd_enable(ah, true); 170ef1b6cd9SSujith Manoharan } 171ef1b6cd9SSujith Manoharan 172ef1b6cd9SSujith Manoharan static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) 173ef1b6cd9SSujith Manoharan { 174ef1b6cd9SSujith Manoharan struct ieee80211_hw *hw = sc->hw; 175ef1b6cd9SSujith Manoharan struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 176ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 177ef1b6cd9SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 178ef1b6cd9SSujith Manoharan struct ath_tx_control txctl; 179ab63cb8bSNicholas Mc Guire unsigned long time_left; 180ef1b6cd9SSujith Manoharan 181ef1b6cd9SSujith Manoharan memset(&txctl, 0, sizeof(txctl)); 182bea843c7SSujith Manoharan txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; 183ef1b6cd9SSujith Manoharan 184ef1b6cd9SSujith Manoharan memset(tx_info, 0, sizeof(*tx_info)); 185bff11766SFelix Fietkau tx_info->band = sc->cur_chandef.chan->band; 186ef1b6cd9SSujith Manoharan tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; 187ef1b6cd9SSujith Manoharan tx_info->control.rates[0].idx = 0; 188ef1b6cd9SSujith Manoharan tx_info->control.rates[0].count = 1; 189ef1b6cd9SSujith Manoharan tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; 190ef1b6cd9SSujith Manoharan tx_info->control.rates[1].idx = -1; 191ef1b6cd9SSujith Manoharan 192ef1b6cd9SSujith Manoharan init_completion(&sc->paprd_complete); 193ef1b6cd9SSujith Manoharan txctl.paprd = BIT(chain); 194ef1b6cd9SSujith Manoharan 195ef1b6cd9SSujith Manoharan if (ath_tx_start(hw, skb, &txctl) != 0) { 196ef1b6cd9SSujith Manoharan ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); 197ef1b6cd9SSujith Manoharan dev_kfree_skb_any(skb); 198ef1b6cd9SSujith Manoharan return false; 199ef1b6cd9SSujith Manoharan } 200ef1b6cd9SSujith Manoharan 201ef1b6cd9SSujith Manoharan time_left = wait_for_completion_timeout(&sc->paprd_complete, 202ef1b6cd9SSujith Manoharan msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); 203ef1b6cd9SSujith Manoharan 204ef1b6cd9SSujith Manoharan if (!time_left) 205ef1b6cd9SSujith Manoharan ath_dbg(common, CALIBRATE, 206ef1b6cd9SSujith Manoharan "Timeout waiting for paprd training on TX chain %d\n", 207ef1b6cd9SSujith Manoharan chain); 208ef1b6cd9SSujith Manoharan 209ef1b6cd9SSujith Manoharan return !!time_left; 210ef1b6cd9SSujith Manoharan } 211ef1b6cd9SSujith Manoharan 212ef1b6cd9SSujith Manoharan void ath_paprd_calibrate(struct work_struct *work) 213ef1b6cd9SSujith Manoharan { 214ef1b6cd9SSujith Manoharan struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); 215ef1b6cd9SSujith Manoharan struct ieee80211_hw *hw = sc->hw; 216ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 217ef1b6cd9SSujith Manoharan struct ieee80211_hdr *hdr; 218ef1b6cd9SSujith Manoharan struct sk_buff *skb = NULL; 219ef1b6cd9SSujith Manoharan struct ath9k_hw_cal_data *caldata = ah->caldata; 220ef1b6cd9SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 221ef1b6cd9SSujith Manoharan int ftype; 222ef1b6cd9SSujith Manoharan int chain_ok = 0; 223ef1b6cd9SSujith Manoharan int chain; 224ef1b6cd9SSujith Manoharan int len = 1800; 225381c726cSFelix Fietkau int ret; 226ef1b6cd9SSujith Manoharan 2274b9b42bfSSujith Manoharan if (!caldata || 2284b9b42bfSSujith Manoharan !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) || 2294b9b42bfSSujith Manoharan test_bit(PAPRD_DONE, &caldata->cal_flags)) { 230914d0f4dSSujith Manoharan ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n"); 231ef1b6cd9SSujith Manoharan return; 232914d0f4dSSujith Manoharan } 233ef1b6cd9SSujith Manoharan 234ef1b6cd9SSujith Manoharan ath9k_ps_wakeup(sc); 235ef1b6cd9SSujith Manoharan 236ef1b6cd9SSujith Manoharan if (ar9003_paprd_init_table(ah) < 0) 237ef1b6cd9SSujith Manoharan goto fail_paprd; 238ef1b6cd9SSujith Manoharan 239ef1b6cd9SSujith Manoharan skb = alloc_skb(len, GFP_KERNEL); 240ef1b6cd9SSujith Manoharan if (!skb) 241ef1b6cd9SSujith Manoharan goto fail_paprd; 242ef1b6cd9SSujith Manoharan 243ef1b6cd9SSujith Manoharan skb_put(skb, len); 244ef1b6cd9SSujith Manoharan memset(skb->data, 0, len); 245ef1b6cd9SSujith Manoharan hdr = (struct ieee80211_hdr *)skb->data; 246ef1b6cd9SSujith Manoharan ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; 247ef1b6cd9SSujith Manoharan hdr->frame_control = cpu_to_le16(ftype); 248ef1b6cd9SSujith Manoharan hdr->duration_id = cpu_to_le16(10); 249ef1b6cd9SSujith Manoharan memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); 250ef1b6cd9SSujith Manoharan memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); 251ef1b6cd9SSujith Manoharan memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); 252ef1b6cd9SSujith Manoharan 253ef1b6cd9SSujith Manoharan for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { 254ef1b6cd9SSujith Manoharan if (!(ah->txchainmask & BIT(chain))) 255ef1b6cd9SSujith Manoharan continue; 256ef1b6cd9SSujith Manoharan 257ef1b6cd9SSujith Manoharan chain_ok = 0; 258ef1b6cd9SSujith Manoharan ar9003_paprd_setup_gain_table(ah, chain); 259ef1b6cd9SSujith Manoharan 260ef1b6cd9SSujith Manoharan ath_dbg(common, CALIBRATE, 261ef1b6cd9SSujith Manoharan "Sending PAPRD training frame on chain %d\n", chain); 262ef1b6cd9SSujith Manoharan if (!ath_paprd_send_frame(sc, skb, chain)) 263ef1b6cd9SSujith Manoharan goto fail_paprd; 264ef1b6cd9SSujith Manoharan 265ef1b6cd9SSujith Manoharan if (!ar9003_paprd_is_done(ah)) { 266ef1b6cd9SSujith Manoharan ath_dbg(common, CALIBRATE, 267ef1b6cd9SSujith Manoharan "PAPRD not yet done on chain %d\n", chain); 268ef1b6cd9SSujith Manoharan break; 269ef1b6cd9SSujith Manoharan } 270ef1b6cd9SSujith Manoharan 271381c726cSFelix Fietkau ret = ar9003_paprd_create_curve(ah, caldata, chain); 272381c726cSFelix Fietkau if (ret == -EINPROGRESS) { 273381c726cSFelix Fietkau ath_dbg(common, CALIBRATE, 274381c726cSFelix Fietkau "PAPRD curve on chain %d needs to be re-trained\n", 275381c726cSFelix Fietkau chain); 276381c726cSFelix Fietkau break; 277381c726cSFelix Fietkau } else if (ret) { 278ef1b6cd9SSujith Manoharan ath_dbg(common, CALIBRATE, 279ef1b6cd9SSujith Manoharan "PAPRD create curve failed on chain %d\n", 280ef1b6cd9SSujith Manoharan chain); 281ef1b6cd9SSujith Manoharan break; 282ef1b6cd9SSujith Manoharan } 283ef1b6cd9SSujith Manoharan 284ef1b6cd9SSujith Manoharan chain_ok = 1; 285ef1b6cd9SSujith Manoharan } 286ef1b6cd9SSujith Manoharan kfree_skb(skb); 287ef1b6cd9SSujith Manoharan 288ef1b6cd9SSujith Manoharan if (chain_ok) { 2894b9b42bfSSujith Manoharan set_bit(PAPRD_DONE, &caldata->cal_flags); 290ef1b6cd9SSujith Manoharan ath_paprd_activate(sc); 291ef1b6cd9SSujith Manoharan } 292ef1b6cd9SSujith Manoharan 293ef1b6cd9SSujith Manoharan fail_paprd: 294ef1b6cd9SSujith Manoharan ath9k_ps_restore(sc); 295ef1b6cd9SSujith Manoharan } 296ef1b6cd9SSujith Manoharan 297ef1b6cd9SSujith Manoharan /* 298ef1b6cd9SSujith Manoharan * ANI performs periodic noise floor calibration 299ef1b6cd9SSujith Manoharan * that is used to adjust and optimize the chip performance. This 300ef1b6cd9SSujith Manoharan * takes environmental changes (location, temperature) into account. 301ef1b6cd9SSujith Manoharan * When the task is complete, it reschedules itself depending on the 302ef1b6cd9SSujith Manoharan * appropriate interval that was calculated. 303ef1b6cd9SSujith Manoharan */ 304*7ac76764SKees Cook void ath_ani_calibrate(struct timer_list *t) 305ef1b6cd9SSujith Manoharan { 306*7ac76764SKees Cook struct ath_common *common = from_timer(common, t, ani.timer); 307*7ac76764SKees Cook struct ath_softc *sc = (struct ath_softc *)common->priv; 308ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 309ef1b6cd9SSujith Manoharan bool longcal = false; 310ef1b6cd9SSujith Manoharan bool shortcal = false; 311ef1b6cd9SSujith Manoharan bool aniflag = false; 312ef1b6cd9SSujith Manoharan unsigned int timestamp = jiffies_to_msecs(jiffies); 313ef1b6cd9SSujith Manoharan u32 cal_interval, short_cal_interval, long_cal_interval; 314ef1b6cd9SSujith Manoharan unsigned long flags; 315ef1b6cd9SSujith Manoharan 3164b9b42bfSSujith Manoharan if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags)) 317ef1b6cd9SSujith Manoharan long_cal_interval = ATH_LONG_CALINTERVAL_INT; 318ef1b6cd9SSujith Manoharan else 319ef1b6cd9SSujith Manoharan long_cal_interval = ATH_LONG_CALINTERVAL; 320ef1b6cd9SSujith Manoharan 321ef1b6cd9SSujith Manoharan short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? 322ef1b6cd9SSujith Manoharan ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; 323ef1b6cd9SSujith Manoharan 324ef1b6cd9SSujith Manoharan /* Only calibrate if awake */ 325424749c7SRajkumar Manoharan if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { 326424749c7SRajkumar Manoharan if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) { 327424749c7SRajkumar Manoharan spin_lock_irqsave(&sc->sc_pm_lock, flags); 328424749c7SRajkumar Manoharan sc->ps_flags |= PS_WAIT_FOR_ANI; 329424749c7SRajkumar Manoharan spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 330424749c7SRajkumar Manoharan } 331ef1b6cd9SSujith Manoharan goto set_timer; 332424749c7SRajkumar Manoharan } 333424749c7SRajkumar Manoharan ah->ani_skip_count = 0; 334424749c7SRajkumar Manoharan spin_lock_irqsave(&sc->sc_pm_lock, flags); 335424749c7SRajkumar Manoharan sc->ps_flags &= ~PS_WAIT_FOR_ANI; 336424749c7SRajkumar Manoharan spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 337ef1b6cd9SSujith Manoharan 338ef1b6cd9SSujith Manoharan ath9k_ps_wakeup(sc); 339ef1b6cd9SSujith Manoharan 340ef1b6cd9SSujith Manoharan /* Long calibration runs independently of short calibration. */ 341ef1b6cd9SSujith Manoharan if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { 342ef1b6cd9SSujith Manoharan longcal = true; 343ef1b6cd9SSujith Manoharan common->ani.longcal_timer = timestamp; 344ef1b6cd9SSujith Manoharan } 345ef1b6cd9SSujith Manoharan 346ef1b6cd9SSujith Manoharan /* Short calibration applies only while caldone is false */ 347ef1b6cd9SSujith Manoharan if (!common->ani.caldone) { 348ef1b6cd9SSujith Manoharan if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { 349ef1b6cd9SSujith Manoharan shortcal = true; 350ef1b6cd9SSujith Manoharan common->ani.shortcal_timer = timestamp; 351ef1b6cd9SSujith Manoharan common->ani.resetcal_timer = timestamp; 352ef1b6cd9SSujith Manoharan } 353ef1b6cd9SSujith Manoharan } else { 354ef1b6cd9SSujith Manoharan if ((timestamp - common->ani.resetcal_timer) >= 355ef1b6cd9SSujith Manoharan ATH_RESTART_CALINTERVAL) { 356ef1b6cd9SSujith Manoharan common->ani.caldone = ath9k_hw_reset_calvalid(ah); 357ef1b6cd9SSujith Manoharan if (common->ani.caldone) 358ef1b6cd9SSujith Manoharan common->ani.resetcal_timer = timestamp; 359ef1b6cd9SSujith Manoharan } 360ef1b6cd9SSujith Manoharan } 361ef1b6cd9SSujith Manoharan 362ef1b6cd9SSujith Manoharan /* Verify whether we must check ANI */ 363e323300dSSujith Manoharan if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) { 364ef1b6cd9SSujith Manoharan aniflag = true; 365ef1b6cd9SSujith Manoharan common->ani.checkani_timer = timestamp; 366ef1b6cd9SSujith Manoharan } 367ef1b6cd9SSujith Manoharan 368ef1b6cd9SSujith Manoharan /* Call ANI routine if necessary */ 369ef1b6cd9SSujith Manoharan if (aniflag) { 370ba24d63dSVille Syrjälä spin_lock_irqsave(&common->cc_lock, flags); 371ef1b6cd9SSujith Manoharan ath9k_hw_ani_monitor(ah, ah->curchan); 372ef1b6cd9SSujith Manoharan ath_update_survey_stats(sc); 373ba24d63dSVille Syrjälä spin_unlock_irqrestore(&common->cc_lock, flags); 374ef1b6cd9SSujith Manoharan } 375ef1b6cd9SSujith Manoharan 376ef1b6cd9SSujith Manoharan /* Perform calibration if necessary */ 377ef1b6cd9SSujith Manoharan if (longcal || shortcal) { 3787b8aaeadSFelix Fietkau int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask, 3797b8aaeadSFelix Fietkau longcal); 3807b8aaeadSFelix Fietkau if (ret < 0) { 3817b8aaeadSFelix Fietkau common->ani.caldone = 0; 3827b8aaeadSFelix Fietkau ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION); 3837b8aaeadSFelix Fietkau return; 3847b8aaeadSFelix Fietkau } 3857b8aaeadSFelix Fietkau 3867b8aaeadSFelix Fietkau common->ani.caldone = ret; 387ef1b6cd9SSujith Manoharan } 388ef1b6cd9SSujith Manoharan 389ef1b6cd9SSujith Manoharan ath_dbg(common, ANI, 390ef1b6cd9SSujith Manoharan "Calibration @%lu finished: %s %s %s, caldone: %s\n", 391ef1b6cd9SSujith Manoharan jiffies, 392ef1b6cd9SSujith Manoharan longcal ? "long" : "", shortcal ? "short" : "", 393ef1b6cd9SSujith Manoharan aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); 394ef1b6cd9SSujith Manoharan 395ef1b6cd9SSujith Manoharan ath9k_ps_restore(sc); 396ef1b6cd9SSujith Manoharan 397ef1b6cd9SSujith Manoharan set_timer: 398ef1b6cd9SSujith Manoharan /* 399ef1b6cd9SSujith Manoharan * Set timer interval based on previous results. 400ef1b6cd9SSujith Manoharan * The interval must be the shortest necessary to satisfy ANI, 401ef1b6cd9SSujith Manoharan * short calibration and long calibration. 402ef1b6cd9SSujith Manoharan */ 403ef1b6cd9SSujith Manoharan cal_interval = ATH_LONG_CALINTERVAL; 404e323300dSSujith Manoharan cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval); 405ef1b6cd9SSujith Manoharan if (!common->ani.caldone) 406ef1b6cd9SSujith Manoharan cal_interval = min(cal_interval, (u32)short_cal_interval); 407ef1b6cd9SSujith Manoharan 408ef1b6cd9SSujith Manoharan mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); 40919f78422SSujith Manoharan 4100f21ee8dSSujith Manoharan if (ar9003_is_paprd_enabled(ah) && ah->caldata) { 4114b9b42bfSSujith Manoharan if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) { 412ef1b6cd9SSujith Manoharan ieee80211_queue_work(sc->hw, &sc->paprd_work); 41319f78422SSujith Manoharan } else if (!ah->paprd_table_write_done) { 41419f78422SSujith Manoharan ath9k_ps_wakeup(sc); 415ef1b6cd9SSujith Manoharan ath_paprd_activate(sc); 41619f78422SSujith Manoharan ath9k_ps_restore(sc); 41719f78422SSujith Manoharan } 418ef1b6cd9SSujith Manoharan } 419ef1b6cd9SSujith Manoharan } 420ef1b6cd9SSujith Manoharan 421da0d45f7SSujith Manoharan void ath_start_ani(struct ath_softc *sc) 422ef1b6cd9SSujith Manoharan { 423da0d45f7SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 424da0d45f7SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 425ef1b6cd9SSujith Manoharan unsigned long timestamp = jiffies_to_msecs(jiffies); 426ef1b6cd9SSujith Manoharan 427da0d45f7SSujith Manoharan if (common->disable_ani || 428eefa01ddSOleksij Rempel !test_bit(ATH_OP_ANI_RUN, &common->op_flags) || 429fbbcd146SFelix Fietkau sc->cur_chan->offchannel) 430ef1b6cd9SSujith Manoharan return; 431ef1b6cd9SSujith Manoharan 432ef1b6cd9SSujith Manoharan common->ani.longcal_timer = timestamp; 433ef1b6cd9SSujith Manoharan common->ani.shortcal_timer = timestamp; 434ef1b6cd9SSujith Manoharan common->ani.checkani_timer = timestamp; 435ef1b6cd9SSujith Manoharan 436da0d45f7SSujith Manoharan ath_dbg(common, ANI, "Starting ANI\n"); 437ef1b6cd9SSujith Manoharan mod_timer(&common->ani.timer, 438ef1b6cd9SSujith Manoharan jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); 439ef1b6cd9SSujith Manoharan } 440ef1b6cd9SSujith Manoharan 441da0d45f7SSujith Manoharan void ath_stop_ani(struct ath_softc *sc) 442da0d45f7SSujith Manoharan { 443da0d45f7SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 444da0d45f7SSujith Manoharan 445da0d45f7SSujith Manoharan ath_dbg(common, ANI, "Stopping ANI\n"); 446da0d45f7SSujith Manoharan del_timer_sync(&common->ani.timer); 447da0d45f7SSujith Manoharan } 448da0d45f7SSujith Manoharan 449da0d45f7SSujith Manoharan void ath_check_ani(struct ath_softc *sc) 450da0d45f7SSujith Manoharan { 451da0d45f7SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 452eefa01ddSOleksij Rempel struct ath_common *common = ath9k_hw_common(sc->sc_ah); 453ca900ac9SRajkumar Manoharan struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; 454da0d45f7SSujith Manoharan 455da0d45f7SSujith Manoharan /* 456da0d45f7SSujith Manoharan * Check for the various conditions in which ANI has to 457da0d45f7SSujith Manoharan * be stopped. 458da0d45f7SSujith Manoharan */ 459da0d45f7SSujith Manoharan if (ah->opmode == NL80211_IFTYPE_ADHOC) { 460da0d45f7SSujith Manoharan if (!cur_conf->enable_beacon) 461da0d45f7SSujith Manoharan goto stop_ani; 462da0d45f7SSujith Manoharan } else if (ah->opmode == NL80211_IFTYPE_AP) { 463da0d45f7SSujith Manoharan if (!cur_conf->enable_beacon) { 464da0d45f7SSujith Manoharan /* 465da0d45f7SSujith Manoharan * Disable ANI only when there are no 466da0d45f7SSujith Manoharan * associated stations. 467da0d45f7SSujith Manoharan */ 468eefa01ddSOleksij Rempel if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) 469da0d45f7SSujith Manoharan goto stop_ani; 470da0d45f7SSujith Manoharan } 471da0d45f7SSujith Manoharan } else if (ah->opmode == NL80211_IFTYPE_STATION) { 472eefa01ddSOleksij Rempel if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) 473da0d45f7SSujith Manoharan goto stop_ani; 474da0d45f7SSujith Manoharan } 475da0d45f7SSujith Manoharan 476eefa01ddSOleksij Rempel if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) { 477eefa01ddSOleksij Rempel set_bit(ATH_OP_ANI_RUN, &common->op_flags); 478da0d45f7SSujith Manoharan ath_start_ani(sc); 479da0d45f7SSujith Manoharan } 480da0d45f7SSujith Manoharan 481da0d45f7SSujith Manoharan return; 482da0d45f7SSujith Manoharan 483da0d45f7SSujith Manoharan stop_ani: 484eefa01ddSOleksij Rempel clear_bit(ATH_OP_ANI_RUN, &common->op_flags); 485da0d45f7SSujith Manoharan ath_stop_ani(sc); 486da0d45f7SSujith Manoharan } 487da0d45f7SSujith Manoharan 488ef1b6cd9SSujith Manoharan void ath_update_survey_nf(struct ath_softc *sc, int channel) 489ef1b6cd9SSujith Manoharan { 490ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 491ef1b6cd9SSujith Manoharan struct ath9k_channel *chan = &ah->channels[channel]; 492ef1b6cd9SSujith Manoharan struct survey_info *survey = &sc->survey[channel]; 493ef1b6cd9SSujith Manoharan 494ef1b6cd9SSujith Manoharan if (chan->noisefloor) { 495ef1b6cd9SSujith Manoharan survey->filled |= SURVEY_INFO_NOISE_DBM; 4965bc225acSLorenzo Bianconi survey->noise = ath9k_hw_getchan_noise(ah, chan, 4975bc225acSLorenzo Bianconi chan->noisefloor); 498ef1b6cd9SSujith Manoharan } 499ef1b6cd9SSujith Manoharan } 500ef1b6cd9SSujith Manoharan 501ef1b6cd9SSujith Manoharan /* 502ef1b6cd9SSujith Manoharan * Updates the survey statistics and returns the busy time since last 503ef1b6cd9SSujith Manoharan * update in %, if the measurement duration was long enough for the 504ef1b6cd9SSujith Manoharan * result to be useful, -1 otherwise. 505ef1b6cd9SSujith Manoharan */ 506ef1b6cd9SSujith Manoharan int ath_update_survey_stats(struct ath_softc *sc) 507ef1b6cd9SSujith Manoharan { 508ef1b6cd9SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 509ef1b6cd9SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 510ef1b6cd9SSujith Manoharan int pos = ah->curchan - &ah->channels[0]; 511ef1b6cd9SSujith Manoharan struct survey_info *survey = &sc->survey[pos]; 512ef1b6cd9SSujith Manoharan struct ath_cycle_counters *cc = &common->cc_survey; 513ef1b6cd9SSujith Manoharan unsigned int div = common->clockrate * 1000; 514ef1b6cd9SSujith Manoharan int ret = 0; 515ef1b6cd9SSujith Manoharan 516ef1b6cd9SSujith Manoharan if (!ah->curchan) 517ef1b6cd9SSujith Manoharan return -1; 518ef1b6cd9SSujith Manoharan 519ef1b6cd9SSujith Manoharan if (ah->power_mode == ATH9K_PM_AWAKE) 520ef1b6cd9SSujith Manoharan ath_hw_cycle_counters_update(common); 521ef1b6cd9SSujith Manoharan 522ef1b6cd9SSujith Manoharan if (cc->cycles > 0) { 5234ed20bebSJohannes Berg survey->filled |= SURVEY_INFO_TIME | 5244ed20bebSJohannes Berg SURVEY_INFO_TIME_BUSY | 5254ed20bebSJohannes Berg SURVEY_INFO_TIME_RX | 5264ed20bebSJohannes Berg SURVEY_INFO_TIME_TX; 5274ed20bebSJohannes Berg survey->time += cc->cycles / div; 5284ed20bebSJohannes Berg survey->time_busy += cc->rx_busy / div; 5294ed20bebSJohannes Berg survey->time_rx += cc->rx_frame / div; 5304ed20bebSJohannes Berg survey->time_tx += cc->tx_frame / div; 531ef1b6cd9SSujith Manoharan } 532ef1b6cd9SSujith Manoharan 533ef1b6cd9SSujith Manoharan if (cc->cycles < div) 534ef1b6cd9SSujith Manoharan return -1; 535ef1b6cd9SSujith Manoharan 536ef1b6cd9SSujith Manoharan if (cc->cycles > 0) 537ef1b6cd9SSujith Manoharan ret = cc->rx_busy * 100 / cc->cycles; 538ef1b6cd9SSujith Manoharan 539ef1b6cd9SSujith Manoharan memset(cc, 0, sizeof(*cc)); 540ef1b6cd9SSujith Manoharan 541ef1b6cd9SSujith Manoharan ath_update_survey_nf(sc, pos); 542ef1b6cd9SSujith Manoharan 543ef1b6cd9SSujith Manoharan return ret; 544ef1b6cd9SSujith Manoharan } 545