Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20bd2a09 authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville
Browse files

ath9k_hw: clean up per-channel calibration data



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: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5ee08656
Loading
Loading
Loading
Loading
+5 −3
Original line number Original line Diff line number Diff line
@@ -63,6 +63,7 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
				      u8 rxchainmask,
				      u8 rxchainmask,
				      struct ath9k_cal_list *currCal)
				      struct ath9k_cal_list *currCal)
{
{
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	bool iscaldone = false;
	bool iscaldone = false;


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


				currCal->calData->calPostProc(ah, numChains);
				currCal->calData->calPostProc(ah, numChains);
				ichan->CalValid |= currCal->calData->calType;
				caldata->CalValid |= currCal->calData->calType;
				currCal->calState = CAL_DONE;
				currCal->calState = CAL_DONE;
				iscaldone = true;
				iscaldone = true;
			} else {
			} else {
				ar9002_hw_setup_calibration(ah, currCal);
				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);
		ath9k_hw_reset_calibration(ah, currCal);
	}
	}


@@ -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);
			ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
	}
	}


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


	return true;
	return true;
}
}
+5 −3
Original line number Original line Diff line number Diff line
@@ -68,6 +68,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
				      u8 rxchainmask,
				      u8 rxchainmask,
				      struct ath9k_cal_list *currCal)
				      struct ath9k_cal_list *currCal)
{
{
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	/* Cal is assumed not done until explicitly set below */
	/* Cal is assumed not done until explicitly set below */
	bool iscaldone = false;
	bool iscaldone = false;


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


				/* Calibration has finished. */
				/* Calibration has finished. */
				ichan->CalValid |= currCal->calData->calType;
				caldata->CalValid |= currCal->calData->calType;
				currCal->calState = CAL_DONE;
				currCal->calState = CAL_DONE;
				iscaldone = true;
				iscaldone = true;
			} else {
			} else {
@@ -106,7 +107,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
			ar9003_hw_setup_calibration(ah, currCal);
			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 */
		/* If current cal is marked invalid in channel, kick it off */
		ath9k_hw_reset_calibration(ah, currCal);
		ath9k_hw_reset_calibration(ah, currCal);
	}
	}
@@ -793,7 +794,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
	if (ah->cal_list_curr)
	if (ah->cal_list_curr)
		ath9k_hw_reset_calibration(ah, 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;
	return true;
}
}
+9 −8
Original line number Original line Diff line number Diff line
@@ -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,
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 *paprd_table_val = caldata->pa_table[chain];
	u32 small_signal_gain = chan->small_signal_gain[chain];
	u32 small_signal_gain = caldata->small_signal_gain[chain];
	u32 training_power;
	u32 training_power;
	u32 reg = 0;
	u32 reg = 0;
	int i;
	int i;
@@ -654,17 +655,17 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
}
}
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);


int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
int ar9003_paprd_create_curve(struct ath_hw *ah,
			      int chain)
			      struct ath9k_hw_cal_data *caldata, int chain)
{
{
	u16 *small_signal_gain = &chan->small_signal_gain[chain];
	u16 *small_signal_gain = &caldata->small_signal_gain[chain];
	u32 *pa_table = chan->pa_table[chain];
	u32 *pa_table = caldata->pa_table[chain];
	u32 *data_L, *data_U;
	u32 *data_L, *data_U;
	int i, status = 0;
	int i, status = 0;
	u32 *buf;
	u32 *buf;
	u32 reg;
	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);
	buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC);
	if (!buf)
	if (!buf)
+1 −0
Original line number Original line Diff line number Diff line
@@ -609,6 +609,7 @@ struct ath_softc {
struct ath_wiphy {
struct ath_wiphy {
	struct ath_softc *sc; /* shared for all virtual wiphys */
	struct ath_softc *sc; /* shared for all virtual wiphys */
	struct ieee80211_hw *hw;
	struct ieee80211_hw *hw;
	struct ath9k_hw_cal_data caldata;
	enum ath_wiphy_state {
	enum ath_wiphy_state {
		ATH_WIPHY_INACTIVE,
		ATH_WIPHY_INACTIVE,
		ATH_WIPHY_ACTIVE,
		ATH_WIPHY_ACTIVE,
+51 −48
Original line number Original line Diff line number Diff line
@@ -22,23 +22,6 @@
/* We can tune this as we go by monitoring really low values */
/* We can tune this as we go by monitoring really low values */
#define ATH9K_NF_TOO_LOW	-60
#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)
static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
{
{
	int16_t nfval;
	int16_t nfval;
@@ -121,6 +104,19 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
	ah->cal_samples = 0;
	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 */
/* This is done for the currently configured channel */
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
{
{
@@ -128,7 +124,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
	struct ieee80211_conf *conf = &common->hw->conf;
	struct ieee80211_conf *conf = &common->hw->conf;
	struct ath9k_cal_list *currCal = ah->cal_list_curr;
	struct ath9k_cal_list *currCal = ah->cal_list_curr;


	if (!ah->curchan)
	if (!ah->caldata)
		return true;
		return true;


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


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


	return false;
	return false;
@@ -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)
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;
	unsigned i, j;
	int32_t val;
	int32_t val;
	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
	struct ath_common *common = ath9k_hw_common(ah);
	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++) {
	for (i = 0; i < NUM_NF_READINGS; i++) {
		if (chainmask & (1 << 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 = REG_READ(ah, ah->nf_regs[i]);
			val &= 0xFFFFFE00;
			val &= 0xFFFFFE00;
			val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
			val |= (((u32) nfval << 1) & 0x1ff);
			REG_WRITE(ah, ah->nf_regs[i], val);
			REG_WRITE(ah, ah->nf_regs[i], val);
		}
		}
	}
	}
@@ -291,14 +296,18 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
	int16_t nfarray[NUM_NF_READINGS] = { 0 };
	int16_t nfarray[NUM_NF_READINGS] = { 0 };
	struct ath9k_nfcal_hist *h;
	struct ath9k_nfcal_hist *h;
	struct ieee80211_channel *c = chan->chan;
	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);
	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) {
		ath_print(common, ATH_DBG_CALIBRATE,
		ath_print(common, ATH_DBG_CALIBRATE,
			  "NF did not complete in calibration window\n");
			  "NF did not complete in calibration window\n");
		nf = 0;
		nf = 0;
		chan->rawNoiseFloor = nf;
		caldata->rawNoiseFloor = nf;
		return chan->rawNoiseFloor;
		return caldata->rawNoiseFloor;
	} 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);
@@ -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);
	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;
	int i, j;


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


	h = ah->caldata->nfCalHist;
	default_nf = ath9k_hw_get_default_nf(ah, chan);
	for (i = 0; i < NUM_NF_READINGS; i++) {
	for (i = 0; i < NUM_NF_READINGS; i++) {
		ah->nfCalHist[i].currIndex = 0;
		h[i].currIndex = 0;
		ah->nfCalHist[i].privNF = limit->nominal;
		h[i].privNF = default_nf;
		ah->nfCalHist[i].invalidNFcount =
		h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
			AR_PHY_CCA_FILTERWINDOW_LENGTH;
		for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
		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 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
{
{
	s16 nf;
	if (!ah->caldata || !ah->caldata->rawNoiseFloor)

		return ath9k_hw_get_default_nf(ah, chan);
	if (chan->rawNoiseFloor == 0)
		nf = -96;
	else
		nf = chan->rawNoiseFloor;

	if (!ath9k_hw_nf_in_range(ah, nf))
		nf = ATH_DEFAULT_NOISE_FLOOR;


	return nf;
	return ah->caldata->rawNoiseFloor;
}
}
EXPORT_SYMBOL(ath9k_hw_getchan_noise);
EXPORT_SYMBOL(ath9k_hw_getchan_noise);
Loading