aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath9k/calib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/calib.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.c224
1 files changed, 153 insertions, 71 deletions
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 45208690c0e..278365b8a89 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008-2009 Atheros Communications Inc.
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,11 +16,10 @@
#include "hw.h"
#include "hw-ops.h"
+#include <linux/export.h>
/* Common calibration code */
-/* We can tune this as we go by monitoring really low values */
-#define ATH9K_NF_TOO_LOW -60
static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
{
@@ -45,12 +44,59 @@ static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
return nfval;
}
-static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
+static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_nf_limits *limit;
+
+ if (!chan || IS_CHAN_2GHZ(chan))
+ limit = &ah->nf_2g;
+ else
+ limit = &ah->nf_5g;
+
+ return limit;
+}
+
+static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+{
+ return ath9k_hw_get_nf_limits(ah, chan)->nominal;
+}
+
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
+ s16 nf)
+{
+ s8 noise = ATH_DEFAULT_NOISE_FLOOR;
+
+ if (nf) {
+ s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
+ ath9k_hw_get_default_nf(ah, chan);
+ if (delta > 0)
+ noise += delta;
+ }
+ return noise;
+}
+EXPORT_SYMBOL(ath9k_hw_getchan_noise);
+
+static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
+ struct ath9k_hw_cal_data *cal,
int16_t *nfarray)
{
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_nf_limits *limit;
+ struct ath9k_nfcal_hist *h;
+ bool high_nf_mid = false;
+ u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
int i;
+ h = cal->nfCalHist;
+ limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
+
for (i = 0; i < NUM_NF_READINGS; i++) {
+ if (!(chainmask & (1 << i)) ||
+ ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan)))
+ continue;
+
h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
@@ -63,7 +109,39 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
h[i].privNF =
ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
}
+
+ if (!h[i].privNF)
+ continue;
+
+ if (h[i].privNF > limit->max) {
+ high_nf_mid = true;
+
+ ath_dbg(common, CALIBRATE,
+ "NFmid[%d] (%d) > MAX (%d), %s\n",
+ i, h[i].privNF, limit->max,
+ (test_bit(NFCAL_INTF, &cal->cal_flags) ?
+ "not corrected (due to interference)" :
+ "correcting to MAX"));
+
+ /*
+ * Normally we limit the average noise floor by the
+ * hardware specific maximum here. However if we have
+ * encountered stuck beacons because of interference,
+ * we bypass this limit here in order to better deal
+ * with our environment.
+ */
+ if (!test_bit(NFCAL_INTF, &cal->cal_flags))
+ h[i].privNF = limit->max;
+ }
}
+
+ /*
+ * If the noise floor seems normal for all chains, assume that
+ * there is no significant interference in the environment anymore.
+ * Re-enable the enforcement of the NF maximum again.
+ */
+ if (!high_nf_mid)
+ clear_bit(NFCAL_INTF, &cal->cal_flags);
}
static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
@@ -104,24 +182,10 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
ah->cal_samples = 0;
}
-static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
- struct ath9k_channel *chan)
-{
- struct ath_nf_limits *limit;
-
- if (!chan || IS_CHAN_2GHZ(chan))
- limit = &ah->nf_2g;
- else
- limit = &ah->nf_5g;
-
- return limit->nominal;
-}
-
/* This is done for the currently configured channel */
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)
@@ -134,18 +198,16 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
return true;
if (currCal->calState != CAL_DONE) {
- ath_print(common, ATH_DBG_CALIBRATE,
- "Calibration state incorrect, %d\n",
- currCal->calState);
+ ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n",
+ currCal->calState);
return true;
}
- if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType))
+ if (!(ah->supp_cals & currCal->calData->calType))
return true;
- ath_print(common, ATH_DBG_CALIBRATE,
- "Resetting Cal %d state for channel %u\n",
- currCal->calData->calType, conf->channel->center_freq);
+ ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
+ currCal->calData->calType, ah->curchan->chan->center_freq);
ah->caldata->CalValid &= ~currCal->calData->calType;
currCal->calState = CAL_WAITING;
@@ -157,7 +219,7 @@ 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,
AR_PHY_AGC_CONTROL_ENABLE_NF);
@@ -188,6 +250,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
if (chainmask & (1 << i)) {
s16 nfval;
+ if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
+ continue;
+
if (h)
nfval = h[i].privNF;
else
@@ -216,7 +281,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
* since 250us often results in NF load timeout and causes deaf
* condition during stress testing 12/12/2009
*/
- for (j = 0; j < 1000; j++) {
+ for (j = 0; j < 10000; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
AR_PHY_AGC_CONTROL_NF) == 0)
break;
@@ -232,10 +297,10 @@ 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 == 1000) {
- ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
- "to load: AR_PHY_AGC_CONTROL=0x%x\n",
- REG_READ(ah, AR_PHY_AGC_CONTROL));
+ if (j == 10000) {
+ 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;
}
@@ -247,6 +312,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
+ 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);
@@ -254,7 +322,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
}
}
REGWRITE_BUFFER_FLUSH(ah);
- DISABLE_REGWRITE_BUFFER(ah);
}
@@ -273,19 +340,19 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
if (!nf[i])
continue;
- ath_print(common, ATH_DBG_CALIBRATE,
- "NF calibrated [%s] [chain %d] is %d\n",
- (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
+ ath_dbg(common, CALIBRATE,
+ "NF calibrated [%s] [chain %d] is %d\n",
+ (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
if (nf[i] > limit->max) {
- ath_print(common, ATH_DBG_CALIBRATE,
- "NF[%d] (%d) > MAX (%d), correcting to MAX",
- i, nf[i], limit->max);
+ ath_dbg(common, CALIBRATE,
+ "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
+ i, nf[i], limit->max);
nf[i] = limit->max;
} else if (nf[i] < limit->min) {
- ath_print(common, ATH_DBG_CALIBRATE,
- "NF[%d] (%d) < MIN (%d), correcting to NOM",
- i, nf[i], limit->min);
+ ath_dbg(common, CALIBRATE,
+ "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
+ i, nf[i], limit->min);
nf[i] = limit->nominal;
}
}
@@ -300,36 +367,35 @@ 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 (!caldata)
+ if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
+ ath_dbg(common, CALIBRATE,
+ "NF did not complete in calibration window\n");
return false;
+ }
- chan->channelFlags &= (~CHANNEL_CW_INT);
- if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
- ath_print(common, ATH_DBG_CALIBRATE,
- "NF did not complete in calibration window\n");
- nf = 0;
- caldata->rawNoiseFloor = nf;
+ ath9k_hw_do_getnf(ah, nfarray);
+ ath9k_hw_nf_sanitize(ah, nfarray);
+ nf = nfarray[0];
+ if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
+ && nf > nfThresh) {
+ ath_dbg(common, CALIBRATE,
+ "noise floor failed detected; detected %d, threshold %d\n",
+ nf, nfThresh);
+ }
+
+ if (!caldata) {
+ chan->noisefloor = nf;
return false;
- } else {
- ath9k_hw_do_getnf(ah, nfarray);
- ath9k_hw_nf_sanitize(ah, nfarray);
- nf = nfarray[0];
- if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
- && nf > nfThresh) {
- ath_print(common, ATH_DBG_CALIBRATE,
- "noise floor failed detected; "
- "detected %d, threshold %d\n",
- nf, nfThresh);
- chan->channelFlags |= CHANNEL_CW_INT;
- }
}
h = caldata->nfCalHist;
- caldata->nfcal_pending = false;
- ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
- caldata->rawNoiseFloor = h[0].privNF;
+ 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, chan->noisefloor);
return true;
}
+EXPORT_SYMBOL(ath9k_hw_getnf);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan)
@@ -338,9 +404,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
s16 default_nf;
int i, j;
- if (!ah->caldata)
- return;
-
+ ah->caldata->channel = chan->channel;
+ ah->caldata->channelFlags = chan->channelFlags;
h = ah->caldata->nfCalHist;
default_nf = ath9k_hw_get_default_nf(ah, chan);
for (i = 0; i < NUM_NF_READINGS; i++) {
@@ -353,11 +418,28 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
}
}
-s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
+
+void ath9k_hw_bstuck_nfcal(struct ath_hw *ah)
{
- if (!ah->caldata || !ah->caldata->rawNoiseFloor)
- return ath9k_hw_get_default_nf(ah, chan);
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
+
+ if (unlikely(!caldata))
+ return;
+
+ /*
+ * If beacons are stuck, the most likely cause is interference.
+ * Triggering a noise floor calibration at this point helps the
+ * hardware adapt to a noisy environment much faster.
+ * To ensure that we recover from stuck beacons quickly, let
+ * the baseband update the internal NF value itself, similar to
+ * what is being done after a full reset.
+ */
+ 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))
+ ath9k_hw_getnf(ah, ah->curchan);
- return ah->caldata->rawNoiseFloor;
+ set_bit(NFCAL_INTF, &caldata->cal_flags);
}
-EXPORT_SYMBOL(ath9k_hw_getchan_noise);
+EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal);
+