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