ath9k_hw: fix a noise floor calibration related race condition

On AR5008-AR9002, other forms of calibration must not be started while
the noise floor calibration is running, as this can create invalid
readings which were sometimes not even recoverable by any further
calibration attempts.

This patch also ensures that the result of noise floor measurements
are processed faster and also allows the result of the initial
calibration on reset to make it into the NF history buffer

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Felix Fietkau 2010-07-31 00:12:01 +02:00 committed by John W. Linville
parent 20bd2a0952
commit 4254bc1c4d
4 changed files with 31 additions and 20 deletions

View File

@ -687,8 +687,13 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
{ {
bool iscaldone = true; bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr; struct ath9k_cal_list *currCal = ah->cal_list_curr;
bool nfcal, nfcal_pending = false;
if (currCal && nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
if (ah->caldata)
nfcal_pending = ah->caldata->nfcal_pending;
if (currCal && !nfcal &&
(currCal->calState == CAL_RUNNING || (currCal->calState == CAL_RUNNING ||
currCal->calState == CAL_WAITING)) { currCal->calState == CAL_WAITING)) {
iscaldone = ar9002_hw_per_calibration(ah, chan, iscaldone = ar9002_hw_per_calibration(ah, chan,
@ -704,7 +709,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
} }
/* Do NF cal only at longer intervals */ /* Do NF cal only at longer intervals */
if (longcal) { if (longcal || nfcal_pending) {
/* Do periodic PAOffset Cal */ /* Do periodic PAOffset Cal */
ar9002_hw_pa_cal(ah, false); ar9002_hw_pa_cal(ah, false);
ar9002_hw_olc_temp_compensation(ah); ar9002_hw_olc_temp_compensation(ah);
@ -713,16 +718,18 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
* Get the value from the previous NF cal and update * Get the value from the previous NF cal and update
* history buffer. * history buffer.
*/ */
ath9k_hw_getnf(ah, chan); if (ath9k_hw_getnf(ah, chan)) {
/*
* Load the NF from history buffer of the current
* channel.
* NF is slow time-variant, so it is OK to use a
* historical value.
*/
ath9k_hw_loadnf(ah, ah->curchan);
}
/* if (longcal)
* Load the NF from history buffer of the current channel. ath9k_hw_start_nfcal(ah, false);
* NF is slow time-variant, so it is OK to use a historical
* value.
*/
ath9k_hw_loadnf(ah, ah->curchan);
ath9k_hw_start_nfcal(ah, false);
} }
return iscaldone; return iscaldone;
@ -872,6 +879,9 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
/* Do NF Calibration after DC offset and other calibrations */ /* Do NF Calibration after DC offset and other calibrations */
ath9k_hw_start_nfcal(ah, true); ath9k_hw_start_nfcal(ah, true);
if (ah->caldata)
ah->caldata->nfcal_pending = true;
ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
/* Enable IQ, ADC Gain and ADC DC offset CALs */ /* Enable IQ, ADC Gain and ADC DC offset CALs */

View File

@ -156,6 +156,9 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
{ {
if (ah->caldata)
ah->caldata->nfcal_pending = true;
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_ENABLE_NF); AR_PHY_AGC_CONTROL_ENABLE_NF);
@ -288,8 +291,7 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
} }
} }
int16_t ath9k_hw_getnf(struct ath_hw *ah, bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
struct ath9k_channel *chan)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
int16_t nf, nfThresh; int16_t nf, nfThresh;
@ -299,7 +301,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
struct ath9k_hw_cal_data *caldata = ah->caldata; struct ath9k_hw_cal_data *caldata = ah->caldata;
if (!caldata) if (!caldata)
return ath9k_hw_get_default_nf(ah, chan); return false;
chan->channelFlags &= (~CHANNEL_CW_INT); chan->channelFlags &= (~CHANNEL_CW_INT);
if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
@ -307,7 +309,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
"NF did not complete in calibration window\n"); "NF did not complete in calibration window\n");
nf = 0; nf = 0;
caldata->rawNoiseFloor = nf; caldata->rawNoiseFloor = nf;
return caldata->rawNoiseFloor; return false;
} else { } else {
ath9k_hw_do_getnf(ah, nfarray); ath9k_hw_do_getnf(ah, nfarray);
ath9k_hw_nf_sanitize(ah, nfarray); ath9k_hw_nf_sanitize(ah, nfarray);
@ -323,11 +325,10 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
} }
h = caldata->nfCalHist; h = caldata->nfCalHist;
caldata->nfcal_pending = false;
ath9k_hw_update_nfcal_hist_buffer(h, nfarray); ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
caldata->rawNoiseFloor = h[0].privNF; caldata->rawNoiseFloor = h[0].privNF;
return true;
return ah->caldata->rawNoiseFloor;
} }
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,

View File

@ -110,8 +110,7 @@ struct ath9k_pacal_info{
bool ath9k_hw_reset_calvalid(struct ath_hw *ah); bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
int16_t ath9k_hw_getnf(struct ath_hw *ah, bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan); struct ath9k_channel *chan);
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);

View File

@ -354,6 +354,7 @@ struct ath9k_hw_cal_data {
int8_t qCoff; int8_t qCoff;
int16_t rawNoiseFloor; int16_t rawNoiseFloor;
bool paprd_done; bool paprd_done;
bool nfcal_pending;
u16 small_signal_gain[AR9300_MAX_CHAINS]; u16 small_signal_gain[AR9300_MAX_CHAINS];
u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];