162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2012 Qualcomm Atheros, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "ath9k.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * TX polling - checks if the TX engine is stuck somewhere
2162306a36Sopenharmony_ci * and issues a chip reset if so.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistatic bool ath_tx_complete_check(struct ath_softc *sc)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct ath_txq *txq;
2662306a36Sopenharmony_ci	int i;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (sc->tx99_state)
2962306a36Sopenharmony_ci		return true;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
3262306a36Sopenharmony_ci		txq = sc->tx.txq_map[i];
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		ath_txq_lock(sc, txq);
3562306a36Sopenharmony_ci		if (txq->axq_depth) {
3662306a36Sopenharmony_ci			if (txq->axq_tx_inprogress) {
3762306a36Sopenharmony_ci				ath_txq_unlock(sc, txq);
3862306a36Sopenharmony_ci				goto reset;
3962306a36Sopenharmony_ci			}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci			txq->axq_tx_inprogress = true;
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci		ath_txq_unlock(sc, txq);
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return true;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cireset:
4962306a36Sopenharmony_ci	ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
5062306a36Sopenharmony_ci		"tx hung, resetting the chip\n");
5162306a36Sopenharmony_ci	ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
5262306a36Sopenharmony_ci	return false;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_civoid ath_hw_check_work(struct work_struct *work)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct ath_softc *sc = container_of(work, struct ath_softc,
5962306a36Sopenharmony_ci					    hw_check_work.work);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!ath_hw_check(sc) ||
6262306a36Sopenharmony_ci	    !ath_tx_complete_check(sc))
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
6662306a36Sopenharmony_ci				     msecs_to_jiffies(ATH_HW_CHECK_POLL_INT));
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Checks if the BB/MAC is hung.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cibool ath_hw_check(struct ath_softc *sc)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
7562306a36Sopenharmony_ci	enum ath_reset_type type;
7662306a36Sopenharmony_ci	bool is_alive;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	is_alive = ath9k_hw_check_alive(sc->sc_ah);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!is_alive) {
8362306a36Sopenharmony_ci		ath_dbg(common, RESET,
8462306a36Sopenharmony_ci			"HW hang detected, schedule chip reset\n");
8562306a36Sopenharmony_ci		type = RESET_TYPE_MAC_HANG;
8662306a36Sopenharmony_ci		ath9k_queue_reset(sc, type);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ath9k_ps_restore(sc);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return is_alive;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * PLL-WAR for AR9485/AR9340
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	static int count;
10062306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (pll_sqsum >= 0x40000) {
10362306a36Sopenharmony_ci		count++;
10462306a36Sopenharmony_ci		if (count == 3) {
10562306a36Sopenharmony_ci			ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
10662306a36Sopenharmony_ci			ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG);
10762306a36Sopenharmony_ci			count = 0;
10862306a36Sopenharmony_ci			return true;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	} else {
11162306a36Sopenharmony_ci		count = 0;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return false;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid ath_hw_pll_work(struct work_struct *work)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	u32 pll_sqsum;
12062306a36Sopenharmony_ci	struct ath_softc *sc = container_of(work, struct ath_softc,
12162306a36Sopenharmony_ci					    hw_pll_work.work);
12262306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
12362306a36Sopenharmony_ci	/*
12462306a36Sopenharmony_ci	 * ensure that the PLL WAR is executed only
12562306a36Sopenharmony_ci	 * after the STA is associated (or) if the
12662306a36Sopenharmony_ci	 * beaconing had started in interfaces that
12762306a36Sopenharmony_ci	 * uses beacons.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
13062306a36Sopenharmony_ci		return;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (sc->tx99_state)
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
13662306a36Sopenharmony_ci	pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
13762306a36Sopenharmony_ci	ath9k_ps_restore(sc);
13862306a36Sopenharmony_ci	if (ath_hw_pll_rx_hang_check(sc, pll_sqsum))
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
14262306a36Sopenharmony_ci				     msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * PA Pre-distortion.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic void ath_paprd_activate(struct ath_softc *sc)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
15162306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
15262306a36Sopenharmony_ci	struct ath9k_hw_cal_data *caldata = ah->caldata;
15362306a36Sopenharmony_ci	int chain;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) {
15662306a36Sopenharmony_ci		ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n");
15762306a36Sopenharmony_ci		return;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	ar9003_paprd_enable(ah, false);
16162306a36Sopenharmony_ci	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
16262306a36Sopenharmony_ci		if (!(ah->txchainmask & BIT(chain)))
16362306a36Sopenharmony_ci			continue;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		ar9003_paprd_populate_single_table(ah, caldata, chain);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ath_dbg(common, CALIBRATE, "Activating PAPRD\n");
16962306a36Sopenharmony_ci	ar9003_paprd_enable(ah, true);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct ieee80211_hw *hw = sc->hw;
17562306a36Sopenharmony_ci	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
17662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
17762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
17862306a36Sopenharmony_ci	struct ath_tx_control txctl;
17962306a36Sopenharmony_ci	unsigned long time_left;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	memset(&txctl, 0, sizeof(txctl));
18262306a36Sopenharmony_ci	txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	memset(tx_info, 0, sizeof(*tx_info));
18562306a36Sopenharmony_ci	tx_info->band = sc->cur_chandef.chan->band;
18662306a36Sopenharmony_ci	tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
18762306a36Sopenharmony_ci	tx_info->control.rates[0].idx = 0;
18862306a36Sopenharmony_ci	tx_info->control.rates[0].count = 1;
18962306a36Sopenharmony_ci	tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
19062306a36Sopenharmony_ci	tx_info->control.rates[1].idx = -1;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	init_completion(&sc->paprd_complete);
19362306a36Sopenharmony_ci	txctl.paprd = BIT(chain);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (ath_tx_start(hw, skb, &txctl) != 0) {
19662306a36Sopenharmony_ci		ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
19762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
19862306a36Sopenharmony_ci		return false;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	time_left = wait_for_completion_timeout(&sc->paprd_complete,
20262306a36Sopenharmony_ci			msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!time_left)
20562306a36Sopenharmony_ci		ath_dbg(common, CALIBRATE,
20662306a36Sopenharmony_ci			"Timeout waiting for paprd training on TX chain %d\n",
20762306a36Sopenharmony_ci			chain);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return !!time_left;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_civoid ath_paprd_calibrate(struct work_struct *work)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
21562306a36Sopenharmony_ci	struct ieee80211_hw *hw = sc->hw;
21662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
21762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
21862306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
21962306a36Sopenharmony_ci	struct ath9k_hw_cal_data *caldata = ah->caldata;
22062306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
22162306a36Sopenharmony_ci	int ftype;
22262306a36Sopenharmony_ci	int chain_ok = 0;
22362306a36Sopenharmony_ci	int chain;
22462306a36Sopenharmony_ci	int len = 1800;
22562306a36Sopenharmony_ci	int ret;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!caldata ||
22862306a36Sopenharmony_ci	    !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) ||
22962306a36Sopenharmony_ci	    test_bit(PAPRD_DONE, &caldata->cal_flags)) {
23062306a36Sopenharmony_ci		ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n");
23162306a36Sopenharmony_ci		return;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (ar9003_paprd_init_table(ah) < 0)
23762306a36Sopenharmony_ci		goto fail_paprd;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_KERNEL);
24062306a36Sopenharmony_ci	if (!skb)
24162306a36Sopenharmony_ci		goto fail_paprd;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	skb_put(skb, len);
24462306a36Sopenharmony_ci	memset(skb->data, 0, len);
24562306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)skb->data;
24662306a36Sopenharmony_ci	ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
24762306a36Sopenharmony_ci	hdr->frame_control = cpu_to_le16(ftype);
24862306a36Sopenharmony_ci	hdr->duration_id = cpu_to_le16(10);
24962306a36Sopenharmony_ci	memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
25062306a36Sopenharmony_ci	memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
25162306a36Sopenharmony_ci	memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
25462306a36Sopenharmony_ci		if (!(ah->txchainmask & BIT(chain)))
25562306a36Sopenharmony_ci			continue;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		chain_ok = 0;
25862306a36Sopenharmony_ci		ar9003_paprd_setup_gain_table(ah, chain);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		ath_dbg(common, CALIBRATE,
26162306a36Sopenharmony_ci			"Sending PAPRD training frame on chain %d\n", chain);
26262306a36Sopenharmony_ci		if (!ath_paprd_send_frame(sc, skb, chain))
26362306a36Sopenharmony_ci			goto fail_paprd;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		if (!ar9003_paprd_is_done(ah)) {
26662306a36Sopenharmony_ci			ath_dbg(common, CALIBRATE,
26762306a36Sopenharmony_ci				"PAPRD not yet done on chain %d\n", chain);
26862306a36Sopenharmony_ci			break;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		ret = ar9003_paprd_create_curve(ah, caldata, chain);
27262306a36Sopenharmony_ci		if (ret == -EINPROGRESS) {
27362306a36Sopenharmony_ci			ath_dbg(common, CALIBRATE,
27462306a36Sopenharmony_ci				"PAPRD curve on chain %d needs to be re-trained\n",
27562306a36Sopenharmony_ci				chain);
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci		} else if (ret) {
27862306a36Sopenharmony_ci			ath_dbg(common, CALIBRATE,
27962306a36Sopenharmony_ci				"PAPRD create curve failed on chain %d\n",
28062306a36Sopenharmony_ci				chain);
28162306a36Sopenharmony_ci			break;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		chain_ok = 1;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	kfree_skb(skb);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (chain_ok) {
28962306a36Sopenharmony_ci		set_bit(PAPRD_DONE, &caldata->cal_flags);
29062306a36Sopenharmony_ci		ath_paprd_activate(sc);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cifail_paprd:
29462306a36Sopenharmony_ci	ath9k_ps_restore(sc);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/*
29862306a36Sopenharmony_ci *  ANI performs periodic noise floor calibration
29962306a36Sopenharmony_ci *  that is used to adjust and optimize the chip performance.  This
30062306a36Sopenharmony_ci *  takes environmental changes (location, temperature) into account.
30162306a36Sopenharmony_ci *  When the task is complete, it reschedules itself depending on the
30262306a36Sopenharmony_ci *  appropriate interval that was calculated.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_civoid ath_ani_calibrate(struct timer_list *t)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct ath_common *common = from_timer(common, t, ani.timer);
30762306a36Sopenharmony_ci	struct ath_softc *sc = (struct ath_softc *)common->priv;
30862306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
30962306a36Sopenharmony_ci	bool longcal = false;
31062306a36Sopenharmony_ci	bool shortcal = false;
31162306a36Sopenharmony_ci	bool aniflag = false;
31262306a36Sopenharmony_ci	unsigned int timestamp = jiffies_to_msecs(jiffies);
31362306a36Sopenharmony_ci	u32 cal_interval, short_cal_interval, long_cal_interval;
31462306a36Sopenharmony_ci	unsigned long flags;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags))
31762306a36Sopenharmony_ci		long_cal_interval = ATH_LONG_CALINTERVAL_INT;
31862306a36Sopenharmony_ci	else
31962306a36Sopenharmony_ci		long_cal_interval = ATH_LONG_CALINTERVAL;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
32262306a36Sopenharmony_ci		ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* Only calibrate if awake */
32562306a36Sopenharmony_ci	if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
32662306a36Sopenharmony_ci		if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) {
32762306a36Sopenharmony_ci			spin_lock_irqsave(&sc->sc_pm_lock, flags);
32862306a36Sopenharmony_ci			sc->ps_flags |= PS_WAIT_FOR_ANI;
32962306a36Sopenharmony_ci			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci		goto set_timer;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci	ah->ani_skip_count = 0;
33462306a36Sopenharmony_ci	spin_lock_irqsave(&sc->sc_pm_lock, flags);
33562306a36Sopenharmony_ci	sc->ps_flags &= ~PS_WAIT_FOR_ANI;
33662306a36Sopenharmony_ci	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ath9k_ps_wakeup(sc);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Long calibration runs independently of short calibration. */
34162306a36Sopenharmony_ci	if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
34262306a36Sopenharmony_ci		longcal = true;
34362306a36Sopenharmony_ci		common->ani.longcal_timer = timestamp;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* Short calibration applies only while caldone is false */
34762306a36Sopenharmony_ci	if (!common->ani.caldone) {
34862306a36Sopenharmony_ci		if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
34962306a36Sopenharmony_ci			shortcal = true;
35062306a36Sopenharmony_ci			common->ani.shortcal_timer = timestamp;
35162306a36Sopenharmony_ci			common->ani.resetcal_timer = timestamp;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		if ((timestamp - common->ani.resetcal_timer) >=
35562306a36Sopenharmony_ci		    ATH_RESTART_CALINTERVAL) {
35662306a36Sopenharmony_ci			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
35762306a36Sopenharmony_ci			if (common->ani.caldone)
35862306a36Sopenharmony_ci				common->ani.resetcal_timer = timestamp;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* Verify whether we must check ANI */
36362306a36Sopenharmony_ci	if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
36462306a36Sopenharmony_ci		aniflag = true;
36562306a36Sopenharmony_ci		common->ani.checkani_timer = timestamp;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* Call ANI routine if necessary */
36962306a36Sopenharmony_ci	if (aniflag) {
37062306a36Sopenharmony_ci		spin_lock_irqsave(&common->cc_lock, flags);
37162306a36Sopenharmony_ci		ath9k_hw_ani_monitor(ah, ah->curchan);
37262306a36Sopenharmony_ci		ath_update_survey_stats(sc);
37362306a36Sopenharmony_ci		spin_unlock_irqrestore(&common->cc_lock, flags);
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Perform calibration if necessary */
37762306a36Sopenharmony_ci	if (longcal || shortcal) {
37862306a36Sopenharmony_ci		int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
37962306a36Sopenharmony_ci					     longcal);
38062306a36Sopenharmony_ci		if (ret < 0) {
38162306a36Sopenharmony_ci			common->ani.caldone = 0;
38262306a36Sopenharmony_ci			ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
38362306a36Sopenharmony_ci			return;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		common->ani.caldone = ret;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ath_dbg(common, ANI,
39062306a36Sopenharmony_ci		"Calibration @%lu finished: %s %s %s, caldone: %s\n",
39162306a36Sopenharmony_ci		jiffies,
39262306a36Sopenharmony_ci		longcal ? "long" : "", shortcal ? "short" : "",
39362306a36Sopenharmony_ci		aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ath9k_ps_restore(sc);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ciset_timer:
39862306a36Sopenharmony_ci	/*
39962306a36Sopenharmony_ci	* Set timer interval based on previous results.
40062306a36Sopenharmony_ci	* The interval must be the shortest necessary to satisfy ANI,
40162306a36Sopenharmony_ci	* short calibration and long calibration.
40262306a36Sopenharmony_ci	*/
40362306a36Sopenharmony_ci	cal_interval = ATH_LONG_CALINTERVAL;
40462306a36Sopenharmony_ci	cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
40562306a36Sopenharmony_ci	if (!common->ani.caldone)
40662306a36Sopenharmony_ci		cal_interval = min(cal_interval, (u32)short_cal_interval);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (ar9003_is_paprd_enabled(ah) && ah->caldata) {
41162306a36Sopenharmony_ci		if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) {
41262306a36Sopenharmony_ci			ieee80211_queue_work(sc->hw, &sc->paprd_work);
41362306a36Sopenharmony_ci		} else if (!ah->paprd_table_write_done) {
41462306a36Sopenharmony_ci			ath9k_ps_wakeup(sc);
41562306a36Sopenharmony_ci			ath_paprd_activate(sc);
41662306a36Sopenharmony_ci			ath9k_ps_restore(sc);
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_civoid ath_start_ani(struct ath_softc *sc)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
42462306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
42562306a36Sopenharmony_ci	unsigned long timestamp = jiffies_to_msecs(jiffies);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (common->disable_ani ||
42862306a36Sopenharmony_ci	    !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
42962306a36Sopenharmony_ci	    sc->cur_chan->offchannel)
43062306a36Sopenharmony_ci		return;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	common->ani.longcal_timer = timestamp;
43362306a36Sopenharmony_ci	common->ani.shortcal_timer = timestamp;
43462306a36Sopenharmony_ci	common->ani.checkani_timer = timestamp;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	ath_dbg(common, ANI, "Starting ANI\n");
43762306a36Sopenharmony_ci	mod_timer(&common->ani.timer,
43862306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval));
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_civoid ath_stop_ani(struct ath_softc *sc)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ath_dbg(common, ANI, "Stopping ANI\n");
44662306a36Sopenharmony_ci	del_timer_sync(&common->ani.timer);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_civoid ath_check_ani(struct ath_softc *sc)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
45262306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
45362306a36Sopenharmony_ci	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/*
45662306a36Sopenharmony_ci	 * Check for the various conditions in which ANI has to
45762306a36Sopenharmony_ci	 * be stopped.
45862306a36Sopenharmony_ci	 */
45962306a36Sopenharmony_ci	if (ah->opmode == NL80211_IFTYPE_ADHOC) {
46062306a36Sopenharmony_ci		if (!cur_conf->enable_beacon)
46162306a36Sopenharmony_ci			goto stop_ani;
46262306a36Sopenharmony_ci	} else if (ah->opmode == NL80211_IFTYPE_AP) {
46362306a36Sopenharmony_ci		if (!cur_conf->enable_beacon) {
46462306a36Sopenharmony_ci			/*
46562306a36Sopenharmony_ci			 * Disable ANI only when there are no
46662306a36Sopenharmony_ci			 * associated stations.
46762306a36Sopenharmony_ci			 */
46862306a36Sopenharmony_ci			if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
46962306a36Sopenharmony_ci				goto stop_ani;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	} else if (ah->opmode == NL80211_IFTYPE_STATION) {
47262306a36Sopenharmony_ci		if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
47362306a36Sopenharmony_ci			goto stop_ani;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) {
47762306a36Sopenharmony_ci		set_bit(ATH_OP_ANI_RUN, &common->op_flags);
47862306a36Sopenharmony_ci		ath_start_ani(sc);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistop_ani:
48462306a36Sopenharmony_ci	clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
48562306a36Sopenharmony_ci	ath_stop_ani(sc);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_civoid ath_update_survey_nf(struct ath_softc *sc, int channel)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
49162306a36Sopenharmony_ci	struct ath9k_channel *chan = &ah->channels[channel];
49262306a36Sopenharmony_ci	struct survey_info *survey = &sc->survey[channel];
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (chan->noisefloor) {
49562306a36Sopenharmony_ci		survey->filled |= SURVEY_INFO_NOISE_DBM;
49662306a36Sopenharmony_ci		survey->noise = ath9k_hw_getchan_noise(ah, chan,
49762306a36Sopenharmony_ci						       chan->noisefloor);
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/*
50262306a36Sopenharmony_ci * Updates the survey statistics and returns the busy time since last
50362306a36Sopenharmony_ci * update in %, if the measurement duration was long enough for the
50462306a36Sopenharmony_ci * result to be useful, -1 otherwise.
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_ciint ath_update_survey_stats(struct ath_softc *sc)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
50962306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
51062306a36Sopenharmony_ci	int pos = ah->curchan - &ah->channels[0];
51162306a36Sopenharmony_ci	struct survey_info *survey = &sc->survey[pos];
51262306a36Sopenharmony_ci	struct ath_cycle_counters *cc = &common->cc_survey;
51362306a36Sopenharmony_ci	unsigned int div = common->clockrate * 1000;
51462306a36Sopenharmony_ci	int ret = 0;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!ah->curchan)
51762306a36Sopenharmony_ci		return -1;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (ah->power_mode == ATH9K_PM_AWAKE)
52062306a36Sopenharmony_ci		ath_hw_cycle_counters_update(common);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (cc->cycles > 0) {
52362306a36Sopenharmony_ci		survey->filled |= SURVEY_INFO_TIME |
52462306a36Sopenharmony_ci			SURVEY_INFO_TIME_BUSY |
52562306a36Sopenharmony_ci			SURVEY_INFO_TIME_RX |
52662306a36Sopenharmony_ci			SURVEY_INFO_TIME_TX;
52762306a36Sopenharmony_ci		survey->time += cc->cycles / div;
52862306a36Sopenharmony_ci		survey->time_busy += cc->rx_busy / div;
52962306a36Sopenharmony_ci		survey->time_rx += cc->rx_frame / div;
53062306a36Sopenharmony_ci		survey->time_tx += cc->tx_frame / div;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (cc->cycles < div)
53462306a36Sopenharmony_ci		return -1;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (cc->cycles > 0)
53762306a36Sopenharmony_ci		ret = cc->rx_busy * 100 / cc->cycles;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	memset(cc, 0, sizeof(*cc));
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ath_update_survey_nf(sc, pos);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return ret;
54462306a36Sopenharmony_ci}
545