Commit 97fe6420 authored by Sujith Manoharan's avatar Sujith Manoharan Committed by John W. Linville
Browse files

ath9k: Modify IQ calibration for AR955x



IQ calibration post-processing for AR955x is different
from other chips - instead of just doing it as part
of AGC calibration once, it is triggered 3 times and
a median is determined. This patch adds initial support
for changing the calibration behavior for AR955x.

Also, to simplify things, a helper routine to issue/poll
AGC calibration is used.

For non-AR955x chips, the iqcal_idx (which will be used
in subsequent patches) is set to zero.

Signed-off-by: default avatarSujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent adddc0d2
Loading
Loading
Loading
Loading
+59 −22
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@
#define MAX_MEASUREMENT	MAX_IQCAL_MEASUREMENT
#define MAX_MEASUREMENT	MAX_IQCAL_MEASUREMENT
#define MAX_MAG_DELTA	11
#define MAX_MAG_DELTA	11
#define MAX_PHS_DELTA	10
#define MAX_PHS_DELTA	10
#define MAXIQCAL        3


struct coeff {
struct coeff {
	int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
	int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
@@ -797,7 +798,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
	if (q_q_coff > 63)
	if (q_q_coff > 63)
		q_q_coff = 63;
		q_q_coff = 63;


	iqc_coeff[0] = (q_q_coff * 128) + q_i_coff;
	iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff);


	ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n",
	ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n",
		chain_idx, iqc_coeff[0]);
		chain_idx, iqc_coeff[0]);
@@ -828,7 +829,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
	if (q_q_coff > 63)
	if (q_q_coff > 63)
		q_q_coff = 63;
		q_q_coff = 63;


	iqc_coeff[1] = (q_q_coff * 128) + q_i_coff;
	iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff);


	ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
	ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
		chain_idx, iqc_coeff[1]);
		chain_idx, iqc_coeff[1]);
@@ -991,7 +992,9 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
	return true;
	return true;
}
}


static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah,
					  int iqcal_idx,
					  bool is_reusable)
{
{
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath_common *common = ath9k_hw_common(ah);
	const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
	const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
@@ -1410,7 +1413,7 @@ static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah,
	}
	}


	if (txiqcal_done)
	if (txiqcal_done)
		ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
		ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable);
	else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
	else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
		ar9003_hw_tx_iq_cal_reload(ah);
		ar9003_hw_tx_iq_cal_reload(ah);


@@ -1456,6 +1459,29 @@ static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah,
	return true;
	return true;
}
}


static bool do_ar9003_agc_cal(struct ath_hw *ah)
{
	struct ath_common *common = ath9k_hw_common(ah);
	bool status;

	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
		  REG_READ(ah, AR_PHY_AGC_CONTROL) |
		  AR_PHY_AGC_CONTROL_CAL);

	status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
			       AR_PHY_AGC_CONTROL_CAL,
			       0, AH_WAIT_TIMEOUT);
	if (!status) {
		ath_dbg(common, CALIBRATE,
			"offset calibration failed to complete in %d ms,"
			"noisy environment?\n",
			AH_WAIT_TIMEOUT / 1000);
		return false;
	}

	return true;
}

static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
				   struct ath9k_channel *chan)
				   struct ath9k_channel *chan)
{
{
@@ -1464,6 +1490,7 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
	bool txiqcal_done = false;
	bool txiqcal_done = false;
	bool status = true;
	bool status = true;
	bool run_agc_cal = false, sep_iq_cal = false;
	bool run_agc_cal = false, sep_iq_cal = false;
	int i = 0;


	/* Use chip chainmask only for calibration */
	/* Use chip chainmask only for calibration */
	ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
	ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
@@ -1518,26 +1545,36 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
		if (AR_SREV_9330_11(ah))
		if (AR_SREV_9330_11(ah))
			ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));
			ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));


		/* Calibrate the AGC */
		/*
		REG_WRITE(ah, AR_PHY_AGC_CONTROL,
		 * For non-AR9550 chips, we just trigger AGC calibration
			  REG_READ(ah, AR_PHY_AGC_CONTROL) |
		 * in the HW, poll for completion and then process
			  AR_PHY_AGC_CONTROL_CAL);
		 * the results.

		 *
		/* Poll for offset calibration complete */
		 * For AR955x, we run it multiple times and use
		status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
		 * median IQ correction.
				       AR_PHY_AGC_CONTROL_CAL,
		 */
				       0, AH_WAIT_TIMEOUT);
		if (!AR_SREV_9550(ah)) {
	}
			status = do_ar9003_agc_cal(ah);

			if (!status)
	if (!status) {
		ath_dbg(common, CALIBRATE,
			"offset calibration failed to complete in %d ms; noisy environment?\n",
			AH_WAIT_TIMEOUT / 1000);
				return false;
				return false;
	}


			if (txiqcal_done)
			if (txiqcal_done)
		ar9003_hw_tx_iq_cal_post_proc(ah, false);
				ar9003_hw_tx_iq_cal_post_proc(ah, 0, false);
		} else {
			if (!txiqcal_done) {
				status = do_ar9003_agc_cal(ah);
				if (!status)
					return false;
			} else {
				for (i = 0; i < MAXIQCAL; i++) {
					status = do_ar9003_agc_cal(ah);
					if (!status)
						return false;
					ar9003_hw_tx_iq_cal_post_proc(ah, i, false);
				}
			}
		}
	}


	/* Revert chainmask to runtime parameters */
	/* Revert chainmask to runtime parameters */
	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);