Skip to content

Commit

Permalink
ath9k_hw: clean up per-channel calibration data
Browse files Browse the repository at this point in the history
The noise floor history buffer is currently not kept per channel, which
can lead to problems when changing channels from a clean channel to a
noisy one. Also when switching from HT20 to HT40, the noise floor
history buffer is full of measurements, but none of them contain data
for the extension channel, which it needs quite a bit of time to recover
from.

This patch puts all the per-channel calibration data into a single data
structure, and gives the the driver control over whether that is used
per-channel or even not used for some channels.

For ath9k_htc, I decided to keep this per-channel in order to avoid
creating regressions.

For ath9k, the data is kept only for the operating channel, which saves
some space. ath9k_hw takes care of wiping old data when the operating
channel or its channel flags change.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Felix Fietkau authored and John W. Linville committed Aug 4, 2010
1 parent 5ee0865 commit 20bd2a0
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 94 deletions.
8 changes: 5 additions & 3 deletions drivers/net/wireless/ath/ath9k/ar9002_calib.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
u8 rxchainmask,
struct ath9k_cal_list *currCal)
{
struct ath9k_hw_cal_data *caldata = ah->caldata;
bool iscaldone = false;

if (currCal->calState == CAL_RUNNING) {
Expand All @@ -81,14 +82,14 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
}

currCal->calData->calPostProc(ah, numChains);
ichan->CalValid |= currCal->calData->calType;
caldata->CalValid |= currCal->calData->calType;
currCal->calState = CAL_DONE;
iscaldone = true;
} else {
ar9002_hw_setup_calibration(ah, currCal);
}
}
} else if (!(ichan->CalValid & currCal->calData->calType)) {
} else if (!(caldata->CalValid & currCal->calData->calType)) {
ath9k_hw_reset_calibration(ah, currCal);
}

Expand Down Expand Up @@ -900,7 +901,8 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
}

chan->CalValid = 0;
if (ah->caldata)
ah->caldata->CalValid = 0;

return true;
}
Expand Down
8 changes: 5 additions & 3 deletions drivers/net/wireless/ath/ath9k/ar9003_calib.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
u8 rxchainmask,
struct ath9k_cal_list *currCal)
{
struct ath9k_hw_cal_data *caldata = ah->caldata;
/* Cal is assumed not done until explicitly set below */
bool iscaldone = false;

Expand Down Expand Up @@ -95,7 +96,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
currCal->calData->calPostProc(ah, numChains);

/* Calibration has finished. */
ichan->CalValid |= currCal->calData->calType;
caldata->CalValid |= currCal->calData->calType;
currCal->calState = CAL_DONE;
iscaldone = true;
} else {
Expand All @@ -106,7 +107,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
ar9003_hw_setup_calibration(ah, currCal);
}
}
} else if (!(ichan->CalValid & currCal->calData->calType)) {
} else if (!(caldata->CalValid & currCal->calData->calType)) {
/* If current cal is marked invalid in channel, kick it off */
ath9k_hw_reset_calibration(ah, currCal);
}
Expand Down Expand Up @@ -793,7 +794,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
if (ah->cal_list_curr)
ath9k_hw_reset_calibration(ah, ah->cal_list_curr);

chan->CalValid = 0;
if (ah->caldata)
ah->caldata->CalValid = 0;

return true;
}
Expand Down
17 changes: 9 additions & 8 deletions drivers/net/wireless/ath/ath9k/ar9003_paprd.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,11 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
}

void ar9003_paprd_populate_single_table(struct ath_hw *ah,
struct ath9k_channel *chan, int chain)
struct ath9k_hw_cal_data *caldata,
int chain)
{
u32 *paprd_table_val = chan->pa_table[chain];
u32 small_signal_gain = chan->small_signal_gain[chain];
u32 *paprd_table_val = caldata->pa_table[chain];
u32 small_signal_gain = caldata->small_signal_gain[chain];
u32 training_power;
u32 reg = 0;
int i;
Expand Down Expand Up @@ -654,17 +655,17 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
}
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);

int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
int chain)
int ar9003_paprd_create_curve(struct ath_hw *ah,
struct ath9k_hw_cal_data *caldata, int chain)
{
u16 *small_signal_gain = &chan->small_signal_gain[chain];
u32 *pa_table = chan->pa_table[chain];
u16 *small_signal_gain = &caldata->small_signal_gain[chain];
u32 *pa_table = caldata->pa_table[chain];
u32 *data_L, *data_U;
int i, status = 0;
u32 *buf;
u32 reg;

memset(chan->pa_table[chain], 0, sizeof(chan->pa_table[chain]));
memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain]));

buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC);
if (!buf)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ struct ath_softc {
struct ath_wiphy {
struct ath_softc *sc; /* shared for all virtual wiphys */
struct ieee80211_hw *hw;
struct ath9k_hw_cal_data caldata;
enum ath_wiphy_state {
ATH_WIPHY_INACTIVE,
ATH_WIPHY_ACTIVE,
Expand Down
99 changes: 51 additions & 48 deletions drivers/net/wireless/ath/ath9k/calib.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,6 @@
/* We can tune this as we go by monitoring really low values */
#define ATH9K_NF_TOO_LOW -60

/* AR5416 may return very high value (like -31 dBm), in those cases the nf
* is incorrect and we should use the static NF value. Later we can try to
* find out why they are reporting these values */

static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf)
{
if (nf > ATH9K_NF_TOO_LOW) {
ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
"noise floor value detected (%d) is "
"lower than what we think is a "
"reasonable value (%d)\n",
nf, ATH9K_NF_TOO_LOW);
return false;
}
return true;
}

static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
{
int16_t nfval;
Expand Down Expand Up @@ -121,14 +104,27 @@ 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->curchan)
if (!ah->caldata)
return true;

if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
Expand All @@ -151,7 +147,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
"Resetting Cal %d state for channel %u\n",
currCal->calData->calType, conf->channel->center_freq);

ah->curchan->CalValid &= ~currCal->calData->calType;
ah->caldata->CalValid &= ~currCal->calData->calType;
currCal->calState = CAL_WAITING;

return false;
Expand All @@ -175,19 +171,28 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)

void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h;
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);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);

h = ah->nfCalHist;
if (ah->caldata)
h = ah->caldata->nfCalHist;

for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
s16 nfval;

if (h)
nfval = h[i].privNF;
else
nfval = default_nf;

val = REG_READ(ah, ah->nf_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
val |= (((u32) nfval << 1) & 0x1ff);
REG_WRITE(ah, ah->nf_regs[i], val);
}
}
Expand Down Expand Up @@ -291,14 +296,18 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
int16_t nfarray[NUM_NF_READINGS] = { 0 };
struct ath9k_nfcal_hist *h;
struct ieee80211_channel *c = chan->chan;
struct ath9k_hw_cal_data *caldata = ah->caldata;

if (!caldata)
return ath9k_hw_get_default_nf(ah, chan);

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;
chan->rawNoiseFloor = nf;
return chan->rawNoiseFloor;
caldata->rawNoiseFloor = nf;
return caldata->rawNoiseFloor;
} else {
ath9k_hw_do_getnf(ah, nfarray);
ath9k_hw_nf_sanitize(ah, nfarray);
Expand All @@ -313,47 +322,41 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
}
}

h = ah->nfCalHist;
h = caldata->nfCalHist;

ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
chan->rawNoiseFloor = h[0].privNF;
caldata->rawNoiseFloor = h[0].privNF;

return chan->rawNoiseFloor;
return ah->caldata->rawNoiseFloor;
}

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 ath_nf_limits *limit;
struct ath9k_nfcal_hist *h;
s16 default_nf;
int i, j;

if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan))
limit = &ah->nf_2g;
else
limit = &ah->nf_5g;
if (!ah->caldata)
return;

h = ah->caldata->nfCalHist;
default_nf = ath9k_hw_get_default_nf(ah, chan);
for (i = 0; i < NUM_NF_READINGS; i++) {
ah->nfCalHist[i].currIndex = 0;
ah->nfCalHist[i].privNF = limit->nominal;
ah->nfCalHist[i].invalidNFcount =
AR_PHY_CCA_FILTERWINDOW_LENGTH;
h[i].currIndex = 0;
h[i].privNF = default_nf;
h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal;
h[i].nfCalBuffer[j] = default_nf;
}
}
}

s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
{
s16 nf;

if (chan->rawNoiseFloor == 0)
nf = -96;
else
nf = chan->rawNoiseFloor;

if (!ath9k_hw_nf_in_range(ah, nf))
nf = ATH_DEFAULT_NOISE_FLOOR;
if (!ah->caldata || !ah->caldata->rawNoiseFloor)
return ath9k_hw_get_default_nf(ah, chan);

return nf;
return ah->caldata->rawNoiseFloor;
}
EXPORT_SYMBOL(ath9k_hw_getchan_noise);
3 changes: 2 additions & 1 deletion drivers/net/wireless/ath/ath9k/calib.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
int16_t ath9k_hw_getnf(struct ath_hw *ah,
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);
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_hw_reset_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal);
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath9k/htc.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ struct ath9k_htc_priv {
u16 seq_no;
u32 bmiss_cnt;

struct ath9k_hw_cal_data caldata[38];

spinlock_t beacon_lock;

bool tx_queues_stop;
Expand Down
10 changes: 6 additions & 4 deletions drivers/net/wireless/ath/ath9k/htc_drv_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
struct ieee80211_conf *conf = &common->hw->conf;
bool fastcc = true;
struct ieee80211_channel *channel = hw->conf.channel;
struct ath9k_hw_cal_data *caldata;
enum htc_phymode mode;
__be16 htc_mode;
u8 cmd_rsp;
Expand All @@ -149,7 +150,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
priv->ah->curchan->channel,
channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));

ret = ath9k_hw_reset(ah, hchan, fastcc);
caldata = &priv->caldata[channel->hw_value];
ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset channel (%u Mhz) "
Expand Down Expand Up @@ -1028,7 +1030,7 @@ static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);

/* Reset the HW */
ret = ath9k_hw_reset(ah, ah->curchan, false);
ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
Expand Down Expand Up @@ -1091,7 +1093,7 @@ static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);

/* Reset the HW */
ret = ath9k_hw_reset(ah, ah->curchan, false);
ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
Expand Down Expand Up @@ -1179,7 +1181,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
ath9k_hw_configpcipowersave(ah, 0, 0);

ath9k_hw_htc_resetinit(ah);
ret = ath9k_hw_reset(ah, init_channel, false);
ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
Expand Down
Loading

0 comments on commit 20bd2a0

Please sign in to comment.