18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "hw.h"
188c2ecf20Sopenharmony_ci#include "hw-ops.h"
198c2ecf20Sopenharmony_ci#include <linux/export.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Common calibration code */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	int16_t nfval;
278c2ecf20Sopenharmony_ci	int16_t sort[ATH9K_NF_CAL_HIST_MAX];
288c2ecf20Sopenharmony_ci	int i, j;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
318c2ecf20Sopenharmony_ci		sort[i] = nfCalBuffer[i];
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
348c2ecf20Sopenharmony_ci		for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
358c2ecf20Sopenharmony_ci			if (sort[j] > sort[j - 1]) {
368c2ecf20Sopenharmony_ci				nfval = sort[j];
378c2ecf20Sopenharmony_ci				sort[j] = sort[j - 1];
388c2ecf20Sopenharmony_ci				sort[j - 1] = nfval;
398c2ecf20Sopenharmony_ci			}
408c2ecf20Sopenharmony_ci		}
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return nfval;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
488c2ecf20Sopenharmony_ci						    struct ath9k_channel *chan)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct ath_nf_limits *limit;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!chan || IS_CHAN_2GHZ(chan))
538c2ecf20Sopenharmony_ci		limit = &ah->nf_2g;
548c2ecf20Sopenharmony_ci	else
558c2ecf20Sopenharmony_ci		limit = &ah->nf_5g;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return limit;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
618c2ecf20Sopenharmony_ci				   struct ath9k_channel *chan,
628c2ecf20Sopenharmony_ci				   int chain)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	s16 calib_nf = ath9k_hw_get_nf_limits(ah, chan)->cal[chain];
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (calib_nf)
678c2ecf20Sopenharmony_ci		return calib_nf;
688c2ecf20Sopenharmony_ci	else
698c2ecf20Sopenharmony_ci		return ath9k_hw_get_nf_limits(ah, chan)->nominal;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cis16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
738c2ecf20Sopenharmony_ci			   s16 nf)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	s8 noise = ATH_DEFAULT_NOISE_FLOOR;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (nf) {
788c2ecf20Sopenharmony_ci		s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
798c2ecf20Sopenharmony_ci			   ath9k_hw_get_default_nf(ah, chan, 0);
808c2ecf20Sopenharmony_ci		if (delta > 0)
818c2ecf20Sopenharmony_ci			noise += delta;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	return noise;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_getchan_noise);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
888c2ecf20Sopenharmony_ci					      struct ath9k_hw_cal_data *cal,
898c2ecf20Sopenharmony_ci					      int16_t *nfarray)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
928c2ecf20Sopenharmony_ci	struct ath_nf_limits *limit;
938c2ecf20Sopenharmony_ci	struct ath9k_nfcal_hist *h;
948c2ecf20Sopenharmony_ci	bool high_nf_mid = false;
958c2ecf20Sopenharmony_ci	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
968c2ecf20Sopenharmony_ci	int i;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	h = cal->nfCalHist;
998c2ecf20Sopenharmony_ci	limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_NF_READINGS; i++) {
1028c2ecf20Sopenharmony_ci		if (!(chainmask & (1 << i)) ||
1038c2ecf20Sopenharmony_ci		    ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan)))
1048c2ecf20Sopenharmony_ci			continue;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
1098c2ecf20Sopenharmony_ci			h[i].currIndex = 0;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		if (h[i].invalidNFcount > 0) {
1128c2ecf20Sopenharmony_ci			h[i].invalidNFcount--;
1138c2ecf20Sopenharmony_ci			h[i].privNF = nfarray[i];
1148c2ecf20Sopenharmony_ci		} else {
1158c2ecf20Sopenharmony_ci			h[i].privNF =
1168c2ecf20Sopenharmony_ci				ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		if (!h[i].privNF)
1208c2ecf20Sopenharmony_ci			continue;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if (h[i].privNF > limit->max) {
1238c2ecf20Sopenharmony_ci			high_nf_mid = true;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci			ath_dbg(common, CALIBRATE,
1268c2ecf20Sopenharmony_ci				"NFmid[%d] (%d) > MAX (%d), %s\n",
1278c2ecf20Sopenharmony_ci				i, h[i].privNF, limit->max,
1288c2ecf20Sopenharmony_ci				(test_bit(NFCAL_INTF, &cal->cal_flags) ?
1298c2ecf20Sopenharmony_ci				 "not corrected (due to interference)" :
1308c2ecf20Sopenharmony_ci				 "correcting to MAX"));
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci			/*
1338c2ecf20Sopenharmony_ci			 * Normally we limit the average noise floor by the
1348c2ecf20Sopenharmony_ci			 * hardware specific maximum here. However if we have
1358c2ecf20Sopenharmony_ci			 * encountered stuck beacons because of interference,
1368c2ecf20Sopenharmony_ci			 * we bypass this limit here in order to better deal
1378c2ecf20Sopenharmony_ci			 * with our environment.
1388c2ecf20Sopenharmony_ci			 */
1398c2ecf20Sopenharmony_ci			if (!test_bit(NFCAL_INTF, &cal->cal_flags))
1408c2ecf20Sopenharmony_ci				h[i].privNF = limit->max;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci	 * If the noise floor seems normal for all chains, assume that
1468c2ecf20Sopenharmony_ci	 * there is no significant interference in the environment anymore.
1478c2ecf20Sopenharmony_ci	 * Re-enable the enforcement of the NF maximum again.
1488c2ecf20Sopenharmony_ci	 */
1498c2ecf20Sopenharmony_ci	if (!high_nf_mid)
1508c2ecf20Sopenharmony_ci		clear_bit(NFCAL_INTF, &cal->cal_flags);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
1548c2ecf20Sopenharmony_ci				   enum nl80211_band band,
1558c2ecf20Sopenharmony_ci				   int16_t *nft)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	switch (band) {
1588c2ecf20Sopenharmony_ci	case NL80211_BAND_5GHZ:
1598c2ecf20Sopenharmony_ci		*nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5);
1608c2ecf20Sopenharmony_ci		break;
1618c2ecf20Sopenharmony_ci	case NL80211_BAND_2GHZ:
1628c2ecf20Sopenharmony_ci		*nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2);
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	default:
1658c2ecf20Sopenharmony_ci		BUG_ON(1);
1668c2ecf20Sopenharmony_ci		return false;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return true;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid ath9k_hw_reset_calibration(struct ath_hw *ah,
1738c2ecf20Sopenharmony_ci				struct ath9k_cal_list *currCal)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int i;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	ath9k_hw_setup_calibration(ah, currCal);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ah->cal_start_time = jiffies;
1808c2ecf20Sopenharmony_ci	currCal->calState = CAL_RUNNING;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
1838c2ecf20Sopenharmony_ci		ah->meas0.sign[i] = 0;
1848c2ecf20Sopenharmony_ci		ah->meas1.sign[i] = 0;
1858c2ecf20Sopenharmony_ci		ah->meas2.sign[i] = 0;
1868c2ecf20Sopenharmony_ci		ah->meas3.sign[i] = 0;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	ah->cal_samples = 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* This is done for the currently configured channel */
1938c2ecf20Sopenharmony_cibool ath9k_hw_reset_calvalid(struct ath_hw *ah)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
1968c2ecf20Sopenharmony_ci	struct ath9k_cal_list *currCal = ah->cal_list_curr;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!ah->caldata)
1998c2ecf20Sopenharmony_ci		return true;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
2028c2ecf20Sopenharmony_ci		return true;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (currCal == NULL)
2058c2ecf20Sopenharmony_ci		return true;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (currCal->calState != CAL_DONE) {
2088c2ecf20Sopenharmony_ci		ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n",
2098c2ecf20Sopenharmony_ci			currCal->calState);
2108c2ecf20Sopenharmony_ci		return true;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	currCal = ah->cal_list;
2148c2ecf20Sopenharmony_ci	do {
2158c2ecf20Sopenharmony_ci		ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
2168c2ecf20Sopenharmony_ci			currCal->calData->calType,
2178c2ecf20Sopenharmony_ci			ah->curchan->chan->center_freq);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		ah->caldata->CalValid &= ~currCal->calData->calType;
2208c2ecf20Sopenharmony_ci		currCal->calState = CAL_WAITING;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		currCal = currCal->calNext;
2238c2ecf20Sopenharmony_ci	} while (currCal != ah->cal_list);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return false;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_reset_calvalid);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_civoid ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	if (ah->caldata)
2328c2ecf20Sopenharmony_ci		set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
2358c2ecf20Sopenharmony_ci		    AR_PHY_AGC_CONTROL_ENABLE_NF);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (update)
2388c2ecf20Sopenharmony_ci		REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
2398c2ecf20Sopenharmony_ci		    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
2408c2ecf20Sopenharmony_ci	else
2418c2ecf20Sopenharmony_ci		REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
2428c2ecf20Sopenharmony_ci		    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ciint ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct ath9k_nfcal_hist *h = NULL;
2508c2ecf20Sopenharmony_ci	unsigned i, j;
2518c2ecf20Sopenharmony_ci	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
2528c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
2538c2ecf20Sopenharmony_ci	s16 default_nf = ath9k_hw_get_nf_limits(ah, chan)->nominal;
2548c2ecf20Sopenharmony_ci	u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (ah->caldata)
2578c2ecf20Sopenharmony_ci		h = ah->caldata->nfCalHist;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	ENABLE_REG_RMW_BUFFER(ah);
2608c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_NF_READINGS; i++) {
2618c2ecf20Sopenharmony_ci		if (chainmask & (1 << i)) {
2628c2ecf20Sopenharmony_ci			s16 nfval;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci			if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
2658c2ecf20Sopenharmony_ci				continue;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci			if (ah->nf_override)
2688c2ecf20Sopenharmony_ci				nfval = ah->nf_override;
2698c2ecf20Sopenharmony_ci			else if (h)
2708c2ecf20Sopenharmony_ci				nfval = h[i].privNF;
2718c2ecf20Sopenharmony_ci			else {
2728c2ecf20Sopenharmony_ci				/* Try to get calibrated noise floor value */
2738c2ecf20Sopenharmony_ci				nfval =
2748c2ecf20Sopenharmony_ci				    ath9k_hw_get_nf_limits(ah, chan)->cal[i];
2758c2ecf20Sopenharmony_ci				if (nfval > -60 || nfval < -127)
2768c2ecf20Sopenharmony_ci					nfval = default_nf;
2778c2ecf20Sopenharmony_ci			}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci			REG_RMW(ah, ah->nf_regs[i],
2808c2ecf20Sopenharmony_ci				(((u32) nfval << 1) & 0x1ff), 0x1ff);
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/*
2858c2ecf20Sopenharmony_ci	 * stop NF cal if ongoing to ensure NF load completes immediately
2868c2ecf20Sopenharmony_ci	 * (or after end rx/tx frame if ongoing)
2878c2ecf20Sopenharmony_ci	 */
2888c2ecf20Sopenharmony_ci	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
2898c2ecf20Sopenharmony_ci		REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
2908c2ecf20Sopenharmony_ci		REG_RMW_BUFFER_FLUSH(ah);
2918c2ecf20Sopenharmony_ci		ENABLE_REG_RMW_BUFFER(ah);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/*
2958c2ecf20Sopenharmony_ci	 * Load software filtered NF value into baseband internal minCCApwr
2968c2ecf20Sopenharmony_ci	 * variable.
2978c2ecf20Sopenharmony_ci	 */
2988c2ecf20Sopenharmony_ci	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
2998c2ecf20Sopenharmony_ci		    AR_PHY_AGC_CONTROL_ENABLE_NF);
3008c2ecf20Sopenharmony_ci	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
3018c2ecf20Sopenharmony_ci		    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
3028c2ecf20Sopenharmony_ci	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
3038c2ecf20Sopenharmony_ci	REG_RMW_BUFFER_FLUSH(ah);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/*
3068c2ecf20Sopenharmony_ci	 * Wait for load to complete, should be fast, a few 10s of us.
3078c2ecf20Sopenharmony_ci	 * The max delay was changed from an original 250us to 22.2 msec.
3088c2ecf20Sopenharmony_ci	 * This would increase timeout to the longest possible frame
3098c2ecf20Sopenharmony_ci	 * (11n max length 22.1 msec)
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	for (j = 0; j < 22200; j++) {
3128c2ecf20Sopenharmony_ci		if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
3138c2ecf20Sopenharmony_ci			      AR_PHY_AGC_CONTROL_NF) == 0)
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci		udelay(10);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * Restart NF so it can continue.
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
3228c2ecf20Sopenharmony_ci		ENABLE_REG_RMW_BUFFER(ah);
3238c2ecf20Sopenharmony_ci		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
3248c2ecf20Sopenharmony_ci			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
3258c2ecf20Sopenharmony_ci				    AR_PHY_AGC_CONTROL_ENABLE_NF);
3268c2ecf20Sopenharmony_ci		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
3278c2ecf20Sopenharmony_ci			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
3288c2ecf20Sopenharmony_ci				    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
3298c2ecf20Sopenharmony_ci		REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
3308c2ecf20Sopenharmony_ci		REG_RMW_BUFFER_FLUSH(ah);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/*
3348c2ecf20Sopenharmony_ci	 * We timed out waiting for the noisefloor to load, probably due to an
3358c2ecf20Sopenharmony_ci	 * in-progress rx. Simply return here and allow the load plenty of time
3368c2ecf20Sopenharmony_ci	 * to complete before the next calibration interval.  We need to avoid
3378c2ecf20Sopenharmony_ci	 * trying to load -50 (which happens below) while the previous load is
3388c2ecf20Sopenharmony_ci	 * still in progress as this can cause rx deafness. Instead by returning
3398c2ecf20Sopenharmony_ci	 * here, the baseband nf cal will just be capped by our present
3408c2ecf20Sopenharmony_ci	 * noisefloor until the next calibration timer.
3418c2ecf20Sopenharmony_ci	 */
3428c2ecf20Sopenharmony_ci	if (j == 22200) {
3438c2ecf20Sopenharmony_ci		ath_dbg(common, ANY,
3448c2ecf20Sopenharmony_ci			"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
3458c2ecf20Sopenharmony_ci			REG_READ(ah, AR_PHY_AGC_CONTROL));
3468c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/*
3508c2ecf20Sopenharmony_ci	 * Restore maxCCAPower register parameter again so that we're not capped
3518c2ecf20Sopenharmony_ci	 * by the median we just loaded.  This will be initial (and max) value
3528c2ecf20Sopenharmony_ci	 * of next noise floor calibration the baseband does.
3538c2ecf20Sopenharmony_ci	 */
3548c2ecf20Sopenharmony_ci	ENABLE_REG_RMW_BUFFER(ah);
3558c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_NF_READINGS; i++) {
3568c2ecf20Sopenharmony_ci		if (chainmask & (1 << i)) {
3578c2ecf20Sopenharmony_ci			if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
3588c2ecf20Sopenharmony_ci				continue;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci			REG_RMW(ah, ah->nf_regs[i],
3618c2ecf20Sopenharmony_ci					(((u32) (-50) << 1) & 0x1ff), 0x1ff);
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	REG_RMW_BUFFER_FLUSH(ah);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_loadnf);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
3748c2ecf20Sopenharmony_ci	struct ath_nf_limits *limit;
3758c2ecf20Sopenharmony_ci	int i;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (IS_CHAN_2GHZ(ah->curchan))
3788c2ecf20Sopenharmony_ci		limit = &ah->nf_2g;
3798c2ecf20Sopenharmony_ci	else
3808c2ecf20Sopenharmony_ci		limit = &ah->nf_5g;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_NF_READINGS; i++) {
3838c2ecf20Sopenharmony_ci		if (!nf[i])
3848c2ecf20Sopenharmony_ci			continue;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		ath_dbg(common, CALIBRATE,
3878c2ecf20Sopenharmony_ci			"NF calibrated [%s] [chain %d] is %d\n",
3888c2ecf20Sopenharmony_ci			(i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		if (nf[i] > limit->max) {
3918c2ecf20Sopenharmony_ci			ath_dbg(common, CALIBRATE,
3928c2ecf20Sopenharmony_ci				"NF[%d] (%d) > MAX (%d), correcting to MAX\n",
3938c2ecf20Sopenharmony_ci				i, nf[i], limit->max);
3948c2ecf20Sopenharmony_ci			nf[i] = limit->max;
3958c2ecf20Sopenharmony_ci		} else if (nf[i] < limit->min) {
3968c2ecf20Sopenharmony_ci			ath_dbg(common, CALIBRATE,
3978c2ecf20Sopenharmony_ci				"NF[%d] (%d) < MIN (%d), correcting to NOM\n",
3988c2ecf20Sopenharmony_ci				i, nf[i], limit->min);
3998c2ecf20Sopenharmony_ci			nf[i] = limit->nominal;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cibool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
4078c2ecf20Sopenharmony_ci	int16_t nf, nfThresh;
4088c2ecf20Sopenharmony_ci	int16_t nfarray[NUM_NF_READINGS] = { 0 };
4098c2ecf20Sopenharmony_ci	struct ath9k_nfcal_hist *h;
4108c2ecf20Sopenharmony_ci	struct ieee80211_channel *c = chan->chan;
4118c2ecf20Sopenharmony_ci	struct ath9k_hw_cal_data *caldata = ah->caldata;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
4148c2ecf20Sopenharmony_ci		ath_dbg(common, CALIBRATE,
4158c2ecf20Sopenharmony_ci			"NF did not complete in calibration window\n");
4168c2ecf20Sopenharmony_ci		return false;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ath9k_hw_do_getnf(ah, nfarray);
4208c2ecf20Sopenharmony_ci	ath9k_hw_nf_sanitize(ah, nfarray);
4218c2ecf20Sopenharmony_ci	nf = nfarray[0];
4228c2ecf20Sopenharmony_ci	if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
4238c2ecf20Sopenharmony_ci	    && nf > nfThresh) {
4248c2ecf20Sopenharmony_ci		ath_dbg(common, CALIBRATE,
4258c2ecf20Sopenharmony_ci			"noise floor failed detected; detected %d, threshold %d\n",
4268c2ecf20Sopenharmony_ci			nf, nfThresh);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (!caldata) {
4308c2ecf20Sopenharmony_ci		chan->noisefloor = nf;
4318c2ecf20Sopenharmony_ci		return false;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	h = caldata->nfCalHist;
4358c2ecf20Sopenharmony_ci	clear_bit(NFCAL_PENDING, &caldata->cal_flags);
4368c2ecf20Sopenharmony_ci	ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
4378c2ecf20Sopenharmony_ci	chan->noisefloor = h[0].privNF;
4388c2ecf20Sopenharmony_ci	ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
4398c2ecf20Sopenharmony_ci	return true;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_getnf);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_civoid ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
4448c2ecf20Sopenharmony_ci				  struct ath9k_channel *chan)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct ath9k_nfcal_hist *h;
4478c2ecf20Sopenharmony_ci	int i, j, k = 0;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ah->caldata->channel = chan->channel;
4508c2ecf20Sopenharmony_ci	ah->caldata->channelFlags = chan->channelFlags;
4518c2ecf20Sopenharmony_ci	h = ah->caldata->nfCalHist;
4528c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_NF_READINGS; i++) {
4538c2ecf20Sopenharmony_ci		h[i].currIndex = 0;
4548c2ecf20Sopenharmony_ci		h[i].privNF = ath9k_hw_get_default_nf(ah, chan, k);
4558c2ecf20Sopenharmony_ci		h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
4568c2ecf20Sopenharmony_ci		for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++)
4578c2ecf20Sopenharmony_ci			h[i].nfCalBuffer[j] = h[i].privNF;
4588c2ecf20Sopenharmony_ci		if (++k >= AR5416_MAX_CHAINS)
4598c2ecf20Sopenharmony_ci			k = 0;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_civoid ath9k_hw_bstuck_nfcal(struct ath_hw *ah)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct ath9k_hw_cal_data *caldata = ah->caldata;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (unlikely(!caldata))
4698c2ecf20Sopenharmony_ci		return;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * If beacons are stuck, the most likely cause is interference.
4738c2ecf20Sopenharmony_ci	 * Triggering a noise floor calibration at this point helps the
4748c2ecf20Sopenharmony_ci	 * hardware adapt to a noisy environment much faster.
4758c2ecf20Sopenharmony_ci	 * To ensure that we recover from stuck beacons quickly, let
4768c2ecf20Sopenharmony_ci	 * the baseband update the internal NF value itself, similar to
4778c2ecf20Sopenharmony_ci	 * what is being done after a full reset.
4788c2ecf20Sopenharmony_ci	 */
4798c2ecf20Sopenharmony_ci	if (!test_bit(NFCAL_PENDING, &caldata->cal_flags))
4808c2ecf20Sopenharmony_ci		ath9k_hw_start_nfcal(ah, true);
4818c2ecf20Sopenharmony_ci	else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF))
4828c2ecf20Sopenharmony_ci		ath9k_hw_getnf(ah, ah->curchan);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	set_bit(NFCAL_INTF, &caldata->cal_flags);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_bstuck_nfcal);
4878c2ecf20Sopenharmony_ci
488