162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications 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 "hw.h" 1862306a36Sopenharmony_ci#include "hw-ops.h" 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* Common calibration code */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci int16_t nfval; 2762306a36Sopenharmony_ci int16_t sort[ATH9K_NF_CAL_HIST_MAX]; 2862306a36Sopenharmony_ci int i, j; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) 3162306a36Sopenharmony_ci sort[i] = nfCalBuffer[i]; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { 3462306a36Sopenharmony_ci for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { 3562306a36Sopenharmony_ci if (sort[j] > sort[j - 1]) { 3662306a36Sopenharmony_ci nfval = sort[j]; 3762306a36Sopenharmony_ci sort[j] = sort[j - 1]; 3862306a36Sopenharmony_ci sort[j - 1] = nfval; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return nfval; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, 4862306a36Sopenharmony_ci struct ath9k_channel *chan) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct ath_nf_limits *limit; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!chan || IS_CHAN_2GHZ(chan)) 5362306a36Sopenharmony_ci limit = &ah->nf_2g; 5462306a36Sopenharmony_ci else 5562306a36Sopenharmony_ci limit = &ah->nf_5g; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return limit; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic s16 ath9k_hw_get_default_nf(struct ath_hw *ah, 6162306a36Sopenharmony_ci struct ath9k_channel *chan, 6262306a36Sopenharmony_ci int chain) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci s16 calib_nf = ath9k_hw_get_nf_limits(ah, chan)->cal[chain]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (calib_nf) 6762306a36Sopenharmony_ci return calib_nf; 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci return ath9k_hw_get_nf_limits(ah, chan)->nominal; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cis16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, 7362306a36Sopenharmony_ci s16 nf) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci s8 noise = ATH_DEFAULT_NOISE_FLOOR; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (nf) { 7862306a36Sopenharmony_ci s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - 7962306a36Sopenharmony_ci ath9k_hw_get_default_nf(ah, chan, 0); 8062306a36Sopenharmony_ci if (delta > 0) 8162306a36Sopenharmony_ci noise += delta; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci return noise; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_getchan_noise); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, 8862306a36Sopenharmony_ci struct ath9k_hw_cal_data *cal, 8962306a36Sopenharmony_ci int16_t *nfarray) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 9262306a36Sopenharmony_ci struct ath_nf_limits *limit; 9362306a36Sopenharmony_ci struct ath9k_nfcal_hist *h; 9462306a36Sopenharmony_ci bool high_nf_mid = false; 9562306a36Sopenharmony_ci u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; 9662306a36Sopenharmony_ci int i; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci h = cal->nfCalHist; 9962306a36Sopenharmony_ci limit = ath9k_hw_get_nf_limits(ah, ah->curchan); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci for (i = 0; i < NUM_NF_READINGS; i++) { 10262306a36Sopenharmony_ci if (!(chainmask & (1 << i)) || 10362306a36Sopenharmony_ci ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan))) 10462306a36Sopenharmony_ci continue; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) 10962306a36Sopenharmony_ci h[i].currIndex = 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (h[i].invalidNFcount > 0) { 11262306a36Sopenharmony_ci h[i].invalidNFcount--; 11362306a36Sopenharmony_ci h[i].privNF = nfarray[i]; 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci h[i].privNF = 11662306a36Sopenharmony_ci ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!h[i].privNF) 12062306a36Sopenharmony_ci continue; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (h[i].privNF > limit->max) { 12362306a36Sopenharmony_ci high_nf_mid = true; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 12662306a36Sopenharmony_ci "NFmid[%d] (%d) > MAX (%d), %s\n", 12762306a36Sopenharmony_ci i, h[i].privNF, limit->max, 12862306a36Sopenharmony_ci (test_bit(NFCAL_INTF, &cal->cal_flags) ? 12962306a36Sopenharmony_ci "not corrected (due to interference)" : 13062306a36Sopenharmony_ci "correcting to MAX")); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Normally we limit the average noise floor by the 13462306a36Sopenharmony_ci * hardware specific maximum here. However if we have 13562306a36Sopenharmony_ci * encountered stuck beacons because of interference, 13662306a36Sopenharmony_ci * we bypass this limit here in order to better deal 13762306a36Sopenharmony_ci * with our environment. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci if (!test_bit(NFCAL_INTF, &cal->cal_flags)) 14062306a36Sopenharmony_ci h[i].privNF = limit->max; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * If the noise floor seems normal for all chains, assume that 14662306a36Sopenharmony_ci * there is no significant interference in the environment anymore. 14762306a36Sopenharmony_ci * Re-enable the enforcement of the NF maximum again. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (!high_nf_mid) 15062306a36Sopenharmony_ci clear_bit(NFCAL_INTF, &cal->cal_flags); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, 15462306a36Sopenharmony_ci enum nl80211_band band, 15562306a36Sopenharmony_ci int16_t *nft) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci switch (band) { 15862306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 15962306a36Sopenharmony_ci *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 16262306a36Sopenharmony_ci *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci BUG_ON(1); 16662306a36Sopenharmony_ci return false; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return true; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_civoid ath9k_hw_reset_calibration(struct ath_hw *ah, 17362306a36Sopenharmony_ci struct ath9k_cal_list *currCal) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int i; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ath9k_hw_setup_calibration(ah, currCal); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ah->cal_start_time = jiffies; 18062306a36Sopenharmony_ci currCal->calState = CAL_RUNNING; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < AR5416_MAX_CHAINS; i++) { 18362306a36Sopenharmony_ci ah->meas0.sign[i] = 0; 18462306a36Sopenharmony_ci ah->meas1.sign[i] = 0; 18562306a36Sopenharmony_ci ah->meas2.sign[i] = 0; 18662306a36Sopenharmony_ci ah->meas3.sign[i] = 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ah->cal_samples = 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* This is done for the currently configured channel */ 19362306a36Sopenharmony_cibool ath9k_hw_reset_calvalid(struct ath_hw *ah) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 19662306a36Sopenharmony_ci struct ath9k_cal_list *currCal = ah->cal_list_curr; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!ah->caldata) 19962306a36Sopenharmony_ci return true; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) 20262306a36Sopenharmony_ci return true; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (currCal == NULL) 20562306a36Sopenharmony_ci return true; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (currCal->calState != CAL_DONE) { 20862306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n", 20962306a36Sopenharmony_ci currCal->calState); 21062306a36Sopenharmony_ci return true; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci currCal = ah->cal_list; 21462306a36Sopenharmony_ci do { 21562306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", 21662306a36Sopenharmony_ci currCal->calData->calType, 21762306a36Sopenharmony_ci ah->curchan->chan->center_freq); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ah->caldata->CalValid &= ~currCal->calData->calType; 22062306a36Sopenharmony_ci currCal->calState = CAL_WAITING; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci currCal = currCal->calNext; 22362306a36Sopenharmony_ci } while (currCal != ah->cal_list); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return false; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_reset_calvalid); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci if (ah->caldata) 23262306a36Sopenharmony_ci set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), 23562306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_ENABLE_NF); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (update) 23862306a36Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), 23962306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_NO_UPDATE_NF); 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), 24262306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_NO_UPDATE_NF); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct ath9k_nfcal_hist *h = NULL; 25062306a36Sopenharmony_ci unsigned i, j; 25162306a36Sopenharmony_ci u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; 25262306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 25362306a36Sopenharmony_ci s16 default_nf = ath9k_hw_get_nf_limits(ah, chan)->nominal; 25462306a36Sopenharmony_ci u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL(ah)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (ah->caldata) 25762306a36Sopenharmony_ci h = ah->caldata->nfCalHist; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ENABLE_REG_RMW_BUFFER(ah); 26062306a36Sopenharmony_ci for (i = 0; i < NUM_NF_READINGS; i++) { 26162306a36Sopenharmony_ci if (chainmask & (1 << i)) { 26262306a36Sopenharmony_ci s16 nfval; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) 26562306a36Sopenharmony_ci continue; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (ah->nf_override) 26862306a36Sopenharmony_ci nfval = ah->nf_override; 26962306a36Sopenharmony_ci else if (h) 27062306a36Sopenharmony_ci nfval = h[i].privNF; 27162306a36Sopenharmony_ci else { 27262306a36Sopenharmony_ci /* Try to get calibrated noise floor value */ 27362306a36Sopenharmony_ci nfval = 27462306a36Sopenharmony_ci ath9k_hw_get_nf_limits(ah, chan)->cal[i]; 27562306a36Sopenharmony_ci if (nfval > -60 || nfval < -127) 27662306a36Sopenharmony_ci nfval = default_nf; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci REG_RMW(ah, ah->nf_regs[i], 28062306a36Sopenharmony_ci (((u32) nfval << 1) & 0x1ff), 0x1ff); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * stop NF cal if ongoing to ensure NF load completes immediately 28662306a36Sopenharmony_ci * (or after end rx/tx frame if ongoing) 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { 28962306a36Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); 29062306a36Sopenharmony_ci REG_RMW_BUFFER_FLUSH(ah); 29162306a36Sopenharmony_ci ENABLE_REG_RMW_BUFFER(ah); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Load software filtered NF value into baseband internal minCCApwr 29662306a36Sopenharmony_ci * variable. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), 29962306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_ENABLE_NF); 30062306a36Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), 30162306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_NO_UPDATE_NF); 30262306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); 30362306a36Sopenharmony_ci REG_RMW_BUFFER_FLUSH(ah); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * Wait for load to complete, should be fast, a few 10s of us. 30762306a36Sopenharmony_ci * The max delay was changed from an original 250us to 22.2 msec. 30862306a36Sopenharmony_ci * This would increase timeout to the longest possible frame 30962306a36Sopenharmony_ci * (11n max length 22.1 msec) 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci for (j = 0; j < 22200; j++) { 31262306a36Sopenharmony_ci if ((REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & 31362306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_NF) == 0) 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci udelay(10); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * Restart NF so it can continue. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { 32262306a36Sopenharmony_ci ENABLE_REG_RMW_BUFFER(ah); 32362306a36Sopenharmony_ci if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF) 32462306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), 32562306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_ENABLE_NF); 32662306a36Sopenharmony_ci if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF) 32762306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), 32862306a36Sopenharmony_ci AR_PHY_AGC_CONTROL_NO_UPDATE_NF); 32962306a36Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); 33062306a36Sopenharmony_ci REG_RMW_BUFFER_FLUSH(ah); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * We timed out waiting for the noisefloor to load, probably due to an 33562306a36Sopenharmony_ci * in-progress rx. Simply return here and allow the load plenty of time 33662306a36Sopenharmony_ci * to complete before the next calibration interval. We need to avoid 33762306a36Sopenharmony_ci * trying to load -50 (which happens below) while the previous load is 33862306a36Sopenharmony_ci * still in progress as this can cause rx deafness. Instead by returning 33962306a36Sopenharmony_ci * here, the baseband nf cal will just be capped by our present 34062306a36Sopenharmony_ci * noisefloor until the next calibration timer. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (j == 22200) { 34362306a36Sopenharmony_ci ath_dbg(common, ANY, 34462306a36Sopenharmony_ci "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", 34562306a36Sopenharmony_ci REG_READ(ah, AR_PHY_AGC_CONTROL(ah))); 34662306a36Sopenharmony_ci return -ETIMEDOUT; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * Restore maxCCAPower register parameter again so that we're not capped 35162306a36Sopenharmony_ci * by the median we just loaded. This will be initial (and max) value 35262306a36Sopenharmony_ci * of next noise floor calibration the baseband does. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci ENABLE_REG_RMW_BUFFER(ah); 35562306a36Sopenharmony_ci for (i = 0; i < NUM_NF_READINGS; i++) { 35662306a36Sopenharmony_ci if (chainmask & (1 << i)) { 35762306a36Sopenharmony_ci if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) 35862306a36Sopenharmony_ci continue; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci REG_RMW(ah, ah->nf_regs[i], 36162306a36Sopenharmony_ci (((u32) (-50) << 1) & 0x1ff), 0x1ff); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci REG_RMW_BUFFER_FLUSH(ah); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_loadnf); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 37462306a36Sopenharmony_ci struct ath_nf_limits *limit; 37562306a36Sopenharmony_ci int i; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (IS_CHAN_2GHZ(ah->curchan)) 37862306a36Sopenharmony_ci limit = &ah->nf_2g; 37962306a36Sopenharmony_ci else 38062306a36Sopenharmony_ci limit = &ah->nf_5g; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < NUM_NF_READINGS; i++) { 38362306a36Sopenharmony_ci if (!nf[i]) 38462306a36Sopenharmony_ci continue; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 38762306a36Sopenharmony_ci "NF calibrated [%s] [chain %d] is %d\n", 38862306a36Sopenharmony_ci (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (nf[i] > limit->max) { 39162306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 39262306a36Sopenharmony_ci "NF[%d] (%d) > MAX (%d), correcting to MAX\n", 39362306a36Sopenharmony_ci i, nf[i], limit->max); 39462306a36Sopenharmony_ci nf[i] = limit->max; 39562306a36Sopenharmony_ci } else if (nf[i] < limit->min) { 39662306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 39762306a36Sopenharmony_ci "NF[%d] (%d) < MIN (%d), correcting to NOM\n", 39862306a36Sopenharmony_ci i, nf[i], limit->min); 39962306a36Sopenharmony_ci nf[i] = limit->nominal; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cibool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 40762306a36Sopenharmony_ci int16_t nf, nfThresh; 40862306a36Sopenharmony_ci int16_t nfarray[NUM_NF_READINGS] = { 0 }; 40962306a36Sopenharmony_ci struct ath9k_nfcal_hist *h; 41062306a36Sopenharmony_ci struct ieee80211_channel *c = chan->chan; 41162306a36Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & AR_PHY_AGC_CONTROL_NF) { 41462306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 41562306a36Sopenharmony_ci "NF did not complete in calibration window\n"); 41662306a36Sopenharmony_ci return false; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ath9k_hw_do_getnf(ah, nfarray); 42062306a36Sopenharmony_ci ath9k_hw_nf_sanitize(ah, nfarray); 42162306a36Sopenharmony_ci nf = nfarray[0]; 42262306a36Sopenharmony_ci if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) 42362306a36Sopenharmony_ci && nf > nfThresh) { 42462306a36Sopenharmony_ci ath_dbg(common, CALIBRATE, 42562306a36Sopenharmony_ci "noise floor failed detected; detected %d, threshold %d\n", 42662306a36Sopenharmony_ci nf, nfThresh); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!caldata) { 43062306a36Sopenharmony_ci chan->noisefloor = nf; 43162306a36Sopenharmony_ci return false; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci h = caldata->nfCalHist; 43562306a36Sopenharmony_ci clear_bit(NFCAL_PENDING, &caldata->cal_flags); 43662306a36Sopenharmony_ci ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); 43762306a36Sopenharmony_ci chan->noisefloor = h[0].privNF; 43862306a36Sopenharmony_ci ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); 43962306a36Sopenharmony_ci return true; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_getnf); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_civoid ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, 44462306a36Sopenharmony_ci struct ath9k_channel *chan) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct ath9k_nfcal_hist *h; 44762306a36Sopenharmony_ci int i, j, k = 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ah->caldata->channel = chan->channel; 45062306a36Sopenharmony_ci ah->caldata->channelFlags = chan->channelFlags; 45162306a36Sopenharmony_ci h = ah->caldata->nfCalHist; 45262306a36Sopenharmony_ci for (i = 0; i < NUM_NF_READINGS; i++) { 45362306a36Sopenharmony_ci h[i].currIndex = 0; 45462306a36Sopenharmony_ci h[i].privNF = ath9k_hw_get_default_nf(ah, chan, k); 45562306a36Sopenharmony_ci h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH; 45662306a36Sopenharmony_ci for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) 45762306a36Sopenharmony_ci h[i].nfCalBuffer[j] = h[i].privNF; 45862306a36Sopenharmony_ci if (++k >= AR5416_MAX_CHAINS) 45962306a36Sopenharmony_ci k = 0; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_civoid ath9k_hw_bstuck_nfcal(struct ath_hw *ah) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (unlikely(!caldata)) 46962306a36Sopenharmony_ci return; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * If beacons are stuck, the most likely cause is interference. 47362306a36Sopenharmony_ci * Triggering a noise floor calibration at this point helps the 47462306a36Sopenharmony_ci * hardware adapt to a noisy environment much faster. 47562306a36Sopenharmony_ci * To ensure that we recover from stuck beacons quickly, let 47662306a36Sopenharmony_ci * the baseband update the internal NF value itself, similar to 47762306a36Sopenharmony_ci * what is being done after a full reset. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci if (!test_bit(NFCAL_PENDING, &caldata->cal_flags)) 48062306a36Sopenharmony_ci ath9k_hw_start_nfcal(ah, true); 48162306a36Sopenharmony_ci else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & AR_PHY_AGC_CONTROL_NF)) 48262306a36Sopenharmony_ci ath9k_hw_getnf(ah, ah->curchan); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci set_bit(NFCAL_INTF, &caldata->cal_flags); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); 48762306a36Sopenharmony_ci 488