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

Commit 5f0c04ea authored by Rajkumar Manoharan's avatar Rajkumar Manoharan Committed by John W. Linville
Browse files

ath9k_hw: Improve fast channel change for AR9003 chips



In order to reduce the overall scan time, fast channel change
should be implemented properly. This patch adds fast channel
change support across band switch or channel mode switch
instead of doing full chip reset. During the fastcc, tx iqcal
measurements are preserved and will be reloaded after successful
the channel change.

This patch also addressed fast channel issue where the STA can not
see APs in higher than operating channel on 5GHz band after
the association.

Cc: Paul Stewart <pstew@google.com>
Signed-off-by: default avatarRajkumar Manoharan <rmanohar@qca.qualcomm.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a240dc7b
Loading
Loading
Loading
Loading
+67 −5
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@
#include "hw-ops.h"
#include "ar9003_phy.h"

#define MAX_MEASUREMENT	8
#define MAX_MEASUREMENT	MAX_IQCAL_MEASUREMENT
#define MAX_MAG_DELTA	11
#define MAX_PHS_DELTA	10

@@ -663,6 +663,7 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
{
	int i, im, nmeasurement;
	u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
	struct ath9k_hw_cal_data *caldata = ah->caldata;

	memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
	for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
@@ -712,13 +713,21 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
				REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
					AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
					coeff->iqc_coeff[0]);

			if (caldata)
				caldata->tx_corr_coeff[im][i] =
					coeff->iqc_coeff[0];
		}
		if (caldata)
			caldata->num_measures[i] = nmeasurement;
	}

	REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
		      AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
	REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
		      AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
	if (caldata)
		caldata->done_txiqcal_once = true;

	return;

@@ -845,10 +854,55 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
	ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
	return;
}

static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah)
{
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
	int i, im;

	memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
	for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
		tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] =
					AR_PHY_TX_IQCAL_CORR_COEFF_B0(i);
		if (!AR_SREV_9485(ah)) {
			tx_corr_coeff[i * 2][1] =
			tx_corr_coeff[(i * 2) + 1][1] =
					AR_PHY_TX_IQCAL_CORR_COEFF_B1(i);

			tx_corr_coeff[i * 2][2] =
			tx_corr_coeff[(i * 2) + 1][2] =
					AR_PHY_TX_IQCAL_CORR_COEFF_B2(i);
		}
	}

	for (i = 0; i < AR9300_MAX_CHAINS; i++) {
		if (!(ah->txchainmask & (1 << i)))
			continue;

		for (im = 0; im < caldata->num_measures[i]; im++) {
			if ((im % 2) == 0)
				REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
				     AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
				     caldata->tx_corr_coeff[im][i]);
			else
				REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
				     AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
				     caldata->tx_corr_coeff[im][i]);
		}
	}

	REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
		      AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
	REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
		      AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
}

static bool ar9003_hw_init_cal(struct ath_hw *ah,
			       struct ath9k_channel *chan)
{
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath9k_hw_cal_data *caldata = ah->caldata;
	bool txiqcal_done = false;

	/* Do Tx IQ Calibration */
@@ -860,9 +914,15 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
	 * For AR9485 or later chips, TxIQ cal runs as part of
	 * AGC calibration
	 */
	if (AR_SREV_9485_OR_LATER(ah))
	if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah)) {
		if (caldata && !caldata->done_txiqcal_once)
			REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
				    AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
		else
			REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
				    AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
		txiqcal_done = true;
	else {
	} else {
		txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
		REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
		udelay(5);
@@ -884,6 +944,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,

	if (txiqcal_done)
		ar9003_hw_tx_iq_cal_post_proc(ah);
	else if (caldata && caldata->done_txiqcal_once)
		ar9003_hw_tx_iq_cal_reload(ah);

	ath9k_hw_loadnf(ah, chan);
	ath9k_hw_start_nfcal(ah, true);
@@ -912,8 +974,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);

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

	return true;
}
+69 −0
Original line number Diff line number Diff line
@@ -688,6 +688,7 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
	if (AR_SREV_9480(ah))
		ar9003_hw_prog_ini(ah, &ah->ini_BTCOEX_MAX_TXPWR, 1);

	ah->modes_index = modesIndex;
	ar9003_hw_override_ini(ah);
	ar9003_hw_set_channel_regs(ah, chan);
	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
@@ -1247,6 +1248,73 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
	REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
}

static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
				      struct ath9k_channel *chan,
				      u8 *ini_reloaded)
{
	unsigned int regWrites = 0;
	u32 modesIndex;

	switch (chan->chanmode) {
	case CHANNEL_A:
	case CHANNEL_A_HT20:
		modesIndex = 1;
		break;
	case CHANNEL_A_HT40PLUS:
	case CHANNEL_A_HT40MINUS:
		modesIndex = 2;
		break;
	case CHANNEL_G:
	case CHANNEL_G_HT20:
	case CHANNEL_B:
		modesIndex = 4;
		break;
	case CHANNEL_G_HT40PLUS:
	case CHANNEL_G_HT40MINUS:
		modesIndex = 3;
		break;

	default:
		return -EINVAL;
	}

	if (modesIndex == ah->modes_index) {
		*ini_reloaded = false;
		goto set_rfmode;
	}

	ar9003_hw_prog_ini(ah, &ah->iniSOC[ATH_INI_POST], modesIndex);
	ar9003_hw_prog_ini(ah, &ah->iniMac[ATH_INI_POST], modesIndex);
	ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex);
	ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex);
	if (AR_SREV_9480_20(ah))
		ar9003_hw_prog_ini(ah,
				&ah->ini_radio_post_sys2ant,
				modesIndex);

	REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);

	/*
	 * For 5GHz channels requiring Fast Clock, apply
	 * different modal values.
	 */
	if (IS_CHAN_A_FAST_CLOCK(ah, chan))
		REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites);

	if (AR_SREV_9330(ah))
		REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites);

	if (AR_SREV_9340(ah) && !ah->is_clk_25mhz)
		REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites);

	ah->modes_index = modesIndex;
	*ini_reloaded = true;

set_rfmode:
	ar9003_hw_set_rfmode(ah, chan);
	return 0;
}

void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1275,6 +1343,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
	priv_ops->do_getnf = ar9003_hw_do_getnf;
	priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
	priv_ops->set_radar_params = ar9003_hw_set_radar_params;
	priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;

	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
+3 −0
Original line number Diff line number Diff line
@@ -572,6 +572,8 @@

#define AR_PHY_TXGAIN_TABLE      (AR_SM_BASE + 0x300)

#define AR_PHY_TX_IQCAL_CONTROL_0   (AR_SM_BASE + AR_SREV_9485(ah) ? \
						 0x3c4 : 0x444)
#define AR_PHY_TX_IQCAL_CONTROL_1   (AR_SM_BASE + AR_SREV_9485(ah) ? \
						 0x3c8 : 0x448)
#define AR_PHY_TX_IQCAL_START       (AR_SM_BASE + AR_SREV_9485(ah) ? \
@@ -823,6 +825,7 @@
#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT   0x01000000
#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24
#define AR_PHY_CHANNEL_STATUS_RX_CLEAR      0x00000004
#define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL                   0x80000000
#define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT             0x01fc0000
#define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT_S                   18
#define AR_PHY_TX_IQCAL_START_DO_CAL	    0x00000001
+7 −0
Original line number Diff line number Diff line
@@ -205,4 +205,11 @@ static inline void ath9k_hw_setup_calibration(struct ath_hw *ah,
	ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal);
}

static inline int ath9k_hw_fast_chan_change(struct ath_hw *ah,
					    struct ath9k_channel *chan,
					    u8 *ini_reloaded)
{
	return ath9k_hw_private_ops(ah)->fast_chan_change(ah, chan,
							  ini_reloaded);
}
#endif /* ATH9K_HW_OPS_H */
+32 −0
Original line number Diff line number Diff line
@@ -1394,6 +1394,14 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
	struct ath_common *common = ath9k_hw_common(ah);
	u32 qnum;
	int r;
	bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
	bool band_switch, mode_diff;
	u8 ini_reloaded;

	band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
		      (ah->curchan->channelFlags & (CHANNEL_2GHZ |
						    CHANNEL_5GHZ));
	mode_diff = (chan->chanmode != ah->curchan->chanmode);

	for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
		if (ath9k_hw_numtxpending(ah, qnum)) {
@@ -1408,6 +1416,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
		return false;
	}

	if (edma && (band_switch || mode_diff)) {
		ath9k_hw_mark_phy_inactive(ah);
		udelay(5);

		ath9k_hw_init_pll(ah, NULL);

		if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
			ath_err(common, "Failed to do fast channel change\n");
			return false;
		}
	}

	ath9k_hw_set_channel_regs(ah, chan);

	r = ath9k_hw_rf_set_freq(ah, chan);
@@ -1424,6 +1444,16 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,

	ath9k_hw_spur_mitigate_freq(ah, chan);

	if (edma && (band_switch || mode_diff)) {
		if (band_switch || ini_reloaded)
			ah->eep_ops->set_board_values(ah, chan);

		ath9k_hw_init_bb(ah, chan);

		if (band_switch || ini_reloaded)
			ath9k_hw_init_cal(ah, chan);
	}

	return true;
}

@@ -1677,6 +1707,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,

	ath9k_hw_init_bb(ah, chan);

	if (caldata)
		caldata->done_txiqcal_once = false;
	if (!ath9k_hw_init_cal(ah, chan))
		return -EIO;

Loading