diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/calib.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/calib.c | 208 |
1 files changed, 120 insertions, 88 deletions
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 5e8219a91e25..b4ab85bd7895 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -16,32 +16,25 @@ #include "hw.h" #include "hw-ops.h" +#include <linux/sort.h> #include <linux/export.h> /* Common calibration code */ +static int rcmp_i16(const void *x, const void *y) +{ + /* Sort in reverse order. */ + return *(int16_t *)y - *(int16_t *)x; +} static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) { - int16_t nfval; - int16_t sort[ATH9K_NF_CAL_HIST_MAX]; - int i, j; - - for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) - sort[i] = nfCalBuffer[i]; - - for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { - for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { - if (sort[j] > sort[j - 1]) { - nfval = sort[j]; - sort[j] = sort[j - 1]; - sort[j - 1] = nfval; - } - } - } - nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; + int16_t nfcal[ATH9K_NF_CAL_HIST_MAX]; + + memcpy(nfcal, nfCalBuffer, sizeof(nfcal)); + sort(nfcal, ATH9K_NF_CAL_HIST_MAX, sizeof(int16_t), rcmp_i16, NULL); - return nfval; + return nfcal[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; } static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, @@ -58,19 +51,25 @@ static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, } static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, - struct ath9k_channel *chan) + struct ath9k_channel *chan, + int chain) { - return ath9k_hw_get_nf_limits(ah, chan)->nominal; + s16 calib_nf = ath9k_hw_get_nf_limits(ah, chan)->cal[chain]; + + if (calib_nf) + return calib_nf; + else + return ath9k_hw_get_nf_limits(ah, chan)->nominal; } -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; - if (chan && chan->noisefloor) { - s8 delta = chan->noisefloor - - ATH9K_NF_CAL_NOISE_THRESH - - ath9k_hw_get_default_nf(ah, chan); + if (nf) { + s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - + ath9k_hw_get_default_nf(ah, chan, 0); if (delta > 0) noise += delta; } @@ -119,7 +118,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, ath_dbg(common, CALIBRATE, "NFmid[%d] (%d) > MAX (%d), %s\n", i, h[i].privNF, limit->max, - (cal->nfcal_interference ? + (test_bit(NFCAL_INTF, &cal->cal_flags) ? "not corrected (due to interference)" : "correcting to MAX")); @@ -130,7 +129,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * we bypass this limit here in order to better deal * with our environment. */ - if (!cal->nfcal_interference) + if (!test_bit(NFCAL_INTF, &cal->cal_flags)) h[i].privNF = limit->max; } } @@ -141,18 +140,18 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * Re-enable the enforcement of the NF maximum again. */ if (!high_nf_mid) - cal->nfcal_interference = false; + clear_bit(NFCAL_INTF, &cal->cal_flags); } static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, - enum ieee80211_band band, + enum nl80211_band band, int16_t *nft) { switch (band) { - case IEEE80211_BAND_5GHZ: + case NL80211_BAND_5GHZ: *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); break; - case IEEE80211_BAND_2GHZ: + case NL80211_BAND_2GHZ: *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); break; default: @@ -170,6 +169,7 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, ath9k_hw_setup_calibration(ah, currCal); + ah->cal_start_time = jiffies; currCal->calState = CAL_RUNNING; for (i = 0; i < AR5416_MAX_CHAINS; i++) { @@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; if (!ah->caldata) @@ -204,14 +203,17 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; } - if (!(ah->supp_cals & currCal->calData->calType)) - return true; + currCal = ah->cal_list; + do { + ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", + currCal->calData->calType, + ah->curchan->chan->center_freq); - ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", - currCal->calData->calType, conf->chandef.chan->center_freq); + ah->caldata->CalValid &= ~currCal->calData->calType; + currCal->calState = CAL_WAITING; - ah->caldata->CalValid &= ~currCal->calData->calType; - currCal->calState = CAL_WAITING; + currCal = currCal->calNext; + } while (currCal != ah->cal_list); return false; } @@ -220,77 +222,108 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { if (ah->caldata) - ah->caldata->nfcal_pending = true; + set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_ENABLE_NF); if (update) - REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NO_UPDATE_NF); else - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NO_UPDATE_NF); - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); } -void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) +int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath9k_nfcal_hist *h = NULL; unsigned i, j; - int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; - s16 default_nf = ath9k_hw_get_default_nf(ah, chan); + s16 default_nf = ath9k_hw_get_nf_limits(ah, chan)->nominal; + u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL(ah)); if (ah->caldata) h = ah->caldata->nfCalHist; + ENABLE_REG_RMW_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { s16 nfval; - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; - if (h) + if (ah->nf_override) + nfval = ah->nf_override; + else if (h) nfval = h[i].privNF; - else - nfval = default_nf; + else { + /* Try to get calibrated noise floor value */ + nfval = + ath9k_hw_get_nf_limits(ah, chan)->cal[i]; + if (nfval > -60 || nfval < -127) + nfval = default_nf; + } - val = REG_READ(ah, ah->nf_regs[i]); - val &= 0xFFFFFE00; - val |= (((u32) nfval << 1) & 0x1ff); - REG_WRITE(ah, ah->nf_regs[i], val); + REG_RMW(ah, ah->nf_regs[i], + (((u32) nfval << 1) & 0x1ff), 0x1ff); } } /* + * stop NF cal if ongoing to ensure NF load completes immediately + * (or after end rx/tx frame if ongoing) + */ + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); + ENABLE_REG_RMW_BUFFER(ah); + } + + /* * Load software filtered NF value into baseband internal minCCApwr * variable. */ - REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_ENABLE_NF); - REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NO_UPDATE_NF); - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); /* * Wait for load to complete, should be fast, a few 10s of us. - * The max delay was changed from an original 250us to 10000us - * since 250us often results in NF load timeout and causes deaf - * condition during stress testing 12/12/2009 + * The max delay was changed from an original 250us to 22.2 msec. + * This would increase timeout to the longest possible frame + * (11n max length 22.1 msec) */ - for (j = 0; j < 10000; j++) { - if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & - AR_PHY_AGC_CONTROL_NF) == 0) + for (j = 0; j < 22200; j++) { + if ((REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & + AR_PHY_AGC_CONTROL_NF) == 0) break; udelay(10); } /* + * Restart NF so it can continue. + */ + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { + ENABLE_REG_RMW_BUFFER(ah); + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF) + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), + AR_PHY_AGC_CONTROL_ENABLE_NF); + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF) + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah), AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); + } + + /* * We timed out waiting for the noisefloor to load, probably due to an * in-progress rx. Simply return here and allow the load plenty of time * to complete before the next calibration interval. We need to avoid @@ -299,11 +332,11 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) * here, the baseband nf cal will just be capped by our present * noisefloor until the next calibration timer. */ - if (j == 10000) { + if (j == 22200) { ath_dbg(common, ANY, "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", - REG_READ(ah, AR_PHY_AGC_CONTROL)); - return; + REG_READ(ah, AR_PHY_AGC_CONTROL(ah))); + return -ETIMEDOUT; } /* @@ -311,20 +344,21 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ - ENABLE_REGWRITE_BUFFER(ah); + ENABLE_REG_RMW_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; - val = REG_READ(ah, ah->nf_regs[i]); - val &= 0xFFFFFE00; - val |= (((u32) (-50) << 1) & 0x1ff); - REG_WRITE(ah, ah->nf_regs[i], val); + REG_RMW(ah, ah->nf_regs[i], + (((u32) (-50) << 1) & 0x1ff), 0x1ff); } } - REGWRITE_BUFFER_FLUSH(ah); + REG_RMW_BUFFER_FLUSH(ah); + + return 0; } +EXPORT_SYMBOL(ath9k_hw_loadnf); static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) @@ -369,7 +403,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) struct ieee80211_channel *c = chan->chan; struct ath9k_hw_cal_data *caldata = ah->caldata; - if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { + if (REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & AR_PHY_AGC_CONTROL_NF) { ath_dbg(common, CALIBRATE, "NF did not complete in calibration window\n"); return false; @@ -391,10 +425,10 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) } h = caldata->nfCalHist; - caldata->nfcal_pending = false; + clear_bit(NFCAL_PENDING, &caldata->cal_flags); ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); @@ -403,21 +437,19 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath9k_nfcal_hist *h; - s16 default_nf; - int i, j; + int i, j, k = 0; ah->caldata->channel = chan->channel; ah->caldata->channelFlags = chan->channelFlags; - ah->caldata->chanmode = chan->chanmode; h = ah->caldata->nfCalHist; - default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { h[i].currIndex = 0; - h[i].privNF = default_nf; + h[i].privNF = ath9k_hw_get_default_nf(ah, chan, k); h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH; - for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { - h[i].nfCalBuffer[j] = default_nf; - } + for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) + h[i].nfCalBuffer[j] = h[i].privNF; + if (++k >= AR5416_MAX_CHAINS) + k = 0; } } @@ -437,12 +469,12 @@ void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) * the baseband update the internal NF value itself, similar to * what is being done after a full reset. */ - if (!caldata->nfcal_pending) + if (!test_bit(NFCAL_PENDING, &caldata->cal_flags)) ath9k_hw_start_nfcal(ah, true); - else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) + else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) & AR_PHY_AGC_CONTROL_NF)) ath9k_hw_getnf(ah, ah->curchan); - caldata->nfcal_interference = true; + set_bit(NFCAL_INTF, &caldata->cal_flags); } EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); |
