2011-04-22 09:20:44

by Rajkumar Manoharan

[permalink] [raw]
Subject: [PATCH 1/2] ath9k_hw: Fix Tx IQ Calibration hang issue in AR9003 chips

On AR9003 chips, doing three IQ calibrations will possibly cause chip
in stuck state. In noisy environment, chip could receive
a packet during the middle of three calibrations and it causes
the conflict of HW access and the eventual failure. It also
causes IQ calibration outliers which results in poor Tx EVM.
During the issue kernel log was filled with following message

ath: timeout (100000 us) on reg 0xa640: 0x00000001 & 0x00000001 != 0x00000000
ath: timeout (100000 us) on reg 0xa2c4: 0x00158dd9 & 0x00000001 != 0x00000000
ath: Unable to reset channel (2412 MHz), reset status -5
ath: Unable to set channel

Signed-off-by: Rajkumar Manoharan <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9003_calib.c | 350 +++++++++----------------
drivers/net/wireless/ath/ath9k/ar9003_phy.h | 23 +-
drivers/net/wireless/ath/ath9k/reg.h | 2 +
3 files changed, 140 insertions(+), 235 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index 4a4cd88..2cfa892 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -18,13 +18,13 @@
#include "hw-ops.h"
#include "ar9003_phy.h"

-#define MPASS 3
#define MAX_MEASUREMENT 8
-#define MAX_DIFFERENCE 10
+#define MAX_MAG_DELTA 11
+#define MAX_PHS_DELTA 10

struct coeff {
- int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MPASS];
- int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MPASS];
+ int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
+ int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
int iqc_coeff[2];
};

@@ -608,36 +608,48 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
return true;
}

-static bool ar9003_hw_compute_closest_pass_and_avg(int *mp_coeff, int *mp_avg)
+static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
+ int max_delta)
{
- int diff[MPASS];
-
- diff[0] = abs(mp_coeff[0] - mp_coeff[1]);
- diff[1] = abs(mp_coeff[1] - mp_coeff[2]);
- diff[2] = abs(mp_coeff[2] - mp_coeff[0]);
-
- if (diff[0] > MAX_DIFFERENCE &&
- diff[1] > MAX_DIFFERENCE &&
- diff[2] > MAX_DIFFERENCE)
- return false;
+ int mp_max = -64, max_idx = 0;
+ int mp_min = 63, min_idx = 0;
+ int mp_avg = 0, i, outlier_idx = 0;
+
+ /* find min/max mismatch across all calibrated gains */
+ for (i = 0; i < nmeasurement; i++) {
+ mp_avg += mp_coeff[i];
+ if (mp_coeff[i] > mp_max) {
+ mp_max = mp_coeff[i];
+ max_idx = i;
+ } else if (mp_coeff[i] < mp_min) {
+ mp_min = mp_coeff[i];
+ min_idx = i;
+ }
+ }

- if (diff[0] <= diff[1] && diff[0] <= diff[2])
- *mp_avg = (mp_coeff[0] + mp_coeff[1]) / 2;
- else if (diff[1] <= diff[2])
- *mp_avg = (mp_coeff[1] + mp_coeff[2]) / 2;
- else
- *mp_avg = (mp_coeff[2] + mp_coeff[0]) / 2;
+ /* find average (exclude max abs value) */
+ for (i = 0; i < nmeasurement; i++) {
+ if ((abs(mp_coeff[i]) < abs(mp_max)) ||
+ (abs(mp_coeff[i]) < abs(mp_min)))
+ mp_avg += mp_coeff[i];
+ }
+ mp_avg /= (nmeasurement - 1);

- return true;
+ /* detect outlier */
+ if (abs(mp_max - mp_min) > max_delta) {
+ if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg))
+ outlier_idx = max_idx;
+ else
+ outlier_idx = min_idx;
+ }
+ mp_coeff[outlier_idx] = mp_avg;
}

static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
u8 num_chains,
struct coeff *coeff)
{
- struct ath_common *common = ath9k_hw_common(ah);
int i, im, nmeasurement;
- int magnitude, phase;
u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];

memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
@@ -657,37 +669,28 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,

/* Load the average of 2 passes */
for (i = 0; i < num_chains; i++) {
- if (AR_SREV_9485(ah))
- nmeasurement = REG_READ_FIELD(ah,
- AR_PHY_TX_IQCAL_STATUS_B0_9485,
- AR_PHY_CALIBRATED_GAINS_0);
- else
- nmeasurement = REG_READ_FIELD(ah,
- AR_PHY_TX_IQCAL_STATUS_B0,
- AR_PHY_CALIBRATED_GAINS_0);
+ nmeasurement = REG_READ_FIELD(ah,
+ AR_PHY_TX_IQCAL_STATUS_B0,
+ AR_PHY_CALIBRATED_GAINS_0);

if (nmeasurement > MAX_MEASUREMENT)
nmeasurement = MAX_MEASUREMENT;

- for (im = 0; im < nmeasurement; im++) {
- /*
- * Determine which 2 passes are closest and compute avg
- * magnitude
- */
- if (!ar9003_hw_compute_closest_pass_and_avg(coeff->mag_coeff[i][im],
- &magnitude))
- goto disable_txiqcal;
+ /* detect outlier only if nmeasurement > 1 */
+ if (nmeasurement > 1) {
+ /* Detect magnitude outlier */
+ ar9003_hw_detect_outlier(coeff->mag_coeff[i],
+ nmeasurement, MAX_MAG_DELTA);

- /*
- * Determine which 2 passes are closest and compute avg
- * phase
- */
- if (!ar9003_hw_compute_closest_pass_and_avg(coeff->phs_coeff[i][im],
- &phase))
- goto disable_txiqcal;
+ /* Detect phase outlier */
+ ar9003_hw_detect_outlier(coeff->phs_coeff[i],
+ nmeasurement, MAX_PHS_DELTA);
+ }

- coeff->iqc_coeff[0] = (magnitude & 0x7f) |
- ((phase & 0x7f) << 7);
+ for (im = 0; im < nmeasurement; im++) {
+
+ coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) |
+ ((coeff->phs_coeff[i][im] & 0x7f) << 7);

if ((im % 2) == 0)
REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
@@ -707,141 +710,37 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,

return;

-disable_txiqcal:
- REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
- AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x0);
- REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
- AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x0);
-
- ath_dbg(common, ATH_DBG_CALIBRATE, "TX IQ Cal disabled\n");
}

-static void ar9003_hw_tx_iq_cal(struct ath_hw *ah)
+static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- static const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
- AR_PHY_TX_IQCAL_STATUS_B0,
- AR_PHY_TX_IQCAL_STATUS_B1,
- AR_PHY_TX_IQCAL_STATUS_B2,
- };
- static const u32 chan_info_tab[] = {
- AR_PHY_CHAN_INFO_TAB_0,
- AR_PHY_CHAN_INFO_TAB_1,
- AR_PHY_CHAN_INFO_TAB_2,
- };
- struct coeff coeff;
- s32 iq_res[6];
- s32 i, j, ip, im, nmeasurement;
- u8 nchains = get_streams(common->tx_chainmask);
-
- for (ip = 0; ip < MPASS; ip++) {
- REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
- AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
- DELPT);
- REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
- AR_PHY_TX_IQCAL_START_DO_CAL,
- AR_PHY_TX_IQCAL_START_DO_CAL);
-
- if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
- AR_PHY_TX_IQCAL_START_DO_CAL,
- 0, AH_WAIT_TIMEOUT)) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Tx IQ Cal not complete.\n");
- goto TX_IQ_CAL_FAILED;
- }
-
- nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0,
- AR_PHY_CALIBRATED_GAINS_0);
- if (nmeasurement > MAX_MEASUREMENT)
- nmeasurement = MAX_MEASUREMENT;
-
- for (i = 0; i < nchains; i++) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Doing Tx IQ Cal for chain %d.\n", i);
- for (im = 0; im < nmeasurement; im++) {
- if (REG_READ(ah, txiqcal_status[i]) &
- AR_PHY_TX_IQCAL_STATUS_FAILED) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Tx IQ Cal failed for chain %d.\n", i);
- goto TX_IQ_CAL_FAILED;
- }
-
- for (j = 0; j < 3; j++) {
- u8 idx = 2 * j,
- offset = 4 * (3 * im + j);
-
- REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY,
- AR_PHY_CHAN_INFO_TAB_S2_READ,
- 0);
-
- /* 32 bits */
- iq_res[idx] = REG_READ(ah,
- chan_info_tab[i] +
- offset);
-
- REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY,
- AR_PHY_CHAN_INFO_TAB_S2_READ,
- 1);
-
- /* 16 bits */
- iq_res[idx+1] = 0xffff & REG_READ(ah,
- chan_info_tab[i] +
- offset);
-
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n",
- idx, iq_res[idx], idx+1, iq_res[idx+1]);
- }
-
- if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
- coeff.iqc_coeff)) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Failed in calculation of IQ correction.\n");
- goto TX_IQ_CAL_FAILED;
- }
- coeff.mag_coeff[i][im][ip] =
- coeff.iqc_coeff[0] & 0x7f;
- coeff.phs_coeff[i][im][ip] =
- (coeff.iqc_coeff[0] >> 7) & 0x7f;
-
- if (coeff.mag_coeff[i][im][ip] > 63)
- coeff.mag_coeff[i][im][ip] -= 128;
- if (coeff.phs_coeff[i][im][ip] > 63)
- coeff.phs_coeff[i][im][ip] -= 128;
-
- }
- }
- }
-
- ar9003_hw_tx_iqcal_load_avg_2_passes(ah, nchains, &coeff);
-
- return;
-
-TX_IQ_CAL_FAILED:
- ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
-}
-
-static void ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
-{
u8 tx_gain_forced;

- REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1_9485,
- AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT);
tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
AR_PHY_TXGAIN_FORCE);
if (tx_gain_forced)
REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
AR_PHY_TXGAIN_FORCE, 0);

- REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START_9485,
- AR_PHY_TX_IQCAL_START_DO_CAL_9485, 1);
+ REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
+ AR_PHY_TX_IQCAL_START_DO_CAL, 1);
+
+ if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
+ AR_PHY_TX_IQCAL_START_DO_CAL, 0,
+ AH_WAIT_TIMEOUT)) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "Tx IQ Cal is not completed.\n");
+ return false;
+ }
+ return true;
}

static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
- AR_PHY_TX_IQCAL_STATUS_B0_9485,
+ AR_PHY_TX_IQCAL_STATUS_B0,
AR_PHY_TX_IQCAL_STATUS_B1,
AR_PHY_TX_IQCAL_STATUS_B2,
};
@@ -853,7 +752,7 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
struct coeff coeff;
s32 iq_res[6];
u8 num_chains = 0;
- int i, ip, im, j;
+ int i, im, j;
int nmeasurement;

for (i = 0; i < AR9300_MAX_CHAINS; i++) {
@@ -861,71 +760,69 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
num_chains++;
}

- for (ip = 0; ip < MPASS; ip++) {
- for (i = 0; i < num_chains; i++) {
- nmeasurement = REG_READ_FIELD(ah,
- AR_PHY_TX_IQCAL_STATUS_B0_9485,
- AR_PHY_CALIBRATED_GAINS_0);
- if (nmeasurement > MAX_MEASUREMENT)
- nmeasurement = MAX_MEASUREMENT;
+ for (i = 0; i < num_chains; i++) {
+ nmeasurement = REG_READ_FIELD(ah,
+ AR_PHY_TX_IQCAL_STATUS_B0,
+ AR_PHY_CALIBRATED_GAINS_0);
+ if (nmeasurement > MAX_MEASUREMENT)
+ nmeasurement = MAX_MEASUREMENT;

- for (im = 0; im < nmeasurement; im++) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Doing Tx IQ Cal for chain %d.\n", i);
+ for (im = 0; im < nmeasurement; im++) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "Doing Tx IQ Cal for chain %d.\n", i);

- if (REG_READ(ah, txiqcal_status[i]) &
- AR_PHY_TX_IQCAL_STATUS_FAILED) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
+ if (REG_READ(ah, txiqcal_status[i]) &
+ AR_PHY_TX_IQCAL_STATUS_FAILED) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
"Tx IQ Cal failed for chain %d.\n", i);
- goto tx_iqcal_fail;
- }
+ goto tx_iqcal_fail;
+ }

- for (j = 0; j < 3; j++) {
- u32 idx = 2 * j, offset = 4 * (3 * im + j);
+ for (j = 0; j < 3; j++) {
+ u32 idx = 2 * j, offset = 4 * (3 * im + j);

- REG_RMW_FIELD(ah,
+ REG_RMW_FIELD(ah,
AR_PHY_CHAN_INFO_MEMORY,
AR_PHY_CHAN_INFO_TAB_S2_READ,
0);

- /* 32 bits */
- iq_res[idx] = REG_READ(ah,
- chan_info_tab[i] +
- offset);
+ /* 32 bits */
+ iq_res[idx] = REG_READ(ah,
+ chan_info_tab[i] +
+ offset);

- REG_RMW_FIELD(ah,
+ REG_RMW_FIELD(ah,
AR_PHY_CHAN_INFO_MEMORY,
AR_PHY_CHAN_INFO_TAB_S2_READ,
1);

- /* 16 bits */
- iq_res[idx + 1] = 0xffff & REG_READ(ah,
- chan_info_tab[i] + offset);
+ /* 16 bits */
+ iq_res[idx + 1] = 0xffff & REG_READ(ah,
+ chan_info_tab[i] + offset);

- ath_dbg(common, ATH_DBG_CALIBRATE,
- "IQ RES[%d]=0x%x"
- "IQ_RES[%d]=0x%x\n",
- idx, iq_res[idx], idx + 1,
- iq_res[idx + 1]);
- }
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "IQ RES[%d]=0x%x"
+ "IQ_RES[%d]=0x%x\n",
+ idx, iq_res[idx], idx + 1,
+ iq_res[idx + 1]);
+ }

- if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
- coeff.iqc_coeff)) {
- ath_dbg(common, ATH_DBG_CALIBRATE,
- "Failed in calculation of IQ correction.\n");
- goto tx_iqcal_fail;
- }
+ if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
+ coeff.iqc_coeff)) {
+ ath_dbg(common, ATH_DBG_CALIBRATE,
+ "Failed in calculation of \
+ IQ correction.\n");
+ goto tx_iqcal_fail;
+ }

- coeff.mag_coeff[i][im][ip] =
- coeff.iqc_coeff[0] & 0x7f;
- coeff.phs_coeff[i][im][ip] =
- (coeff.iqc_coeff[0] >> 7) & 0x7f;
+ coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f;
+ coeff.phs_coeff[i][im] =
+ (coeff.iqc_coeff[0] >> 7) & 0x7f;

- if (coeff.mag_coeff[i][im][ip] > 63)
- coeff.mag_coeff[i][im][ip] -= 128;
- if (coeff.phs_coeff[i][im][ip] > 63)
- coeff.phs_coeff[i][im][ip] -= 128;
- }
+ if (coeff.mag_coeff[i][im] > 63)
+ coeff.mag_coeff[i][im] -= 128;
+ if (coeff.phs_coeff[i][im] > 63)
+ coeff.phs_coeff[i][im] -= 128;
}
}
ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, &coeff);
@@ -941,6 +838,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
{
struct ath_common *common = ath9k_hw_common(ah);
int val;
+ bool txiqcal_done = false;

val = REG_READ(ah, AR_ENT_OTP);
ath_dbg(common, ATH_DBG_CALIBRATE, "ath9k: AR_ENT_OTP 0x%x\n", val);
@@ -957,14 +855,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
ar9003_hw_set_chain_masks(ah, 0x7, 0x7);

/* Do Tx IQ Calibration */
- if (AR_SREV_9485(ah))
- ar9003_hw_tx_iq_cal_run(ah);
- else
- ar9003_hw_tx_iq_cal(ah);
+ REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
+ AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
+ DELPT);

- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
- udelay(5);
- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+ /*
+ * For AR9485 or later chips, TxIQ cal runs as part of
+ * AGC calibration
+ */
+ if (AR_SREV_9485_OR_LATER(ah))
+ txiqcal_done = true;
+ else {
+ txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
+ udelay(5);
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+ }

/* Calibrate the AGC */
REG_WRITE(ah, AR_PHY_AGC_CONTROL,
@@ -979,7 +885,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
return false;
}

- if (AR_SREV_9485(ah))
+ if (txiqcal_done)
ar9003_hw_tx_iq_cal_post_proc(ah);

/* Revert chainmasks to their original values before NF cal */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 8bdda2c..7d1e8b5 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -548,15 +548,12 @@

#define AR_PHY_TXGAIN_TABLE (AR_SM_BASE + 0x300)

-#define AR_PHY_TX_IQCAL_START_9485 (AR_SM_BASE + 0x3c4)
-#define AR_PHY_TX_IQCAL_START_DO_CAL_9485 0x80000000
-#define AR_PHY_TX_IQCAL_START_DO_CAL_9485_S 31
-#define AR_PHY_TX_IQCAL_CONTROL_1_9485 (AR_SM_BASE + 0x3c8)
-#define AR_PHY_TX_IQCAL_STATUS_B0_9485 (AR_SM_BASE + 0x3f0)
-
-#define AR_PHY_TX_IQCAL_CONTROL_1 (AR_SM_BASE + 0x448)
-#define AR_PHY_TX_IQCAL_START (AR_SM_BASE + 0x440)
-#define AR_PHY_TX_IQCAL_STATUS_B0 (AR_SM_BASE + 0x48c)
+#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) ? \
+ 0x3c4 : 0x440)
+#define AR_PHY_TX_IQCAL_STATUS_B0 (AR_SM_BASE + AR_SREV_9485(ah) ? \
+ 0x3f0 : 0x48c)
#define AR_PHY_TX_IQCAL_CORR_COEFF_B0(_i) (AR_SM_BASE + \
(AR_SREV_9485(ah) ? \
0x3d0 : 0x450) + ((_i) << 2))
@@ -758,10 +755,10 @@
#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_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT 0x01fc0000
-#define AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT_S 18
-#define AR_PHY_TX_IQCAL_START_DO_CAL 0x00000001
-#define AR_PHY_TX_IQCAL_START_DO_CAL_S 0
+#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
+#define AR_PHY_TX_IQCAL_START_DO_CAL_S 0

#define AR_PHY_TX_IQCAL_STATUS_FAILED 0x00000001
#define AR_PHY_CALIBRATED_GAINS_0 0x3e
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 6acbf0e..af09275 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -868,6 +868,8 @@
#define AR_SREV_9485_11(_ah) \
(AR_SREV_9485(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11))
+#define AR_SREV_9485_OR_LATER(_ah) \
+ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))

#define AR_SREV_9285E_20(_ah) \
(AR_SREV_9285_12_OR_LATER(_ah) && \
--
1.7.4.4



2011-04-22 09:20:51

by Rajkumar Manoharan

[permalink] [raw]
Subject: [PATCH 2/2] ath9k: set beacon related ps flags on bss_info change

Requesting beacon sync up to configure beacon timers properly
in hw, has be done after doing beacon config with default values.
Setting the flags in beacon config is causing the device to not
enter into network sleep on idle state.

Signed-off-by: Rajkumar Manoharan <[email protected]>
---
drivers/net/wireless/ath/ath9k/beacon.c | 6 ------
drivers/net/wireless/ath/ath9k/main.c | 6 ++++++
2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 24f565b..22cd241 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -781,12 +781,6 @@ void ath_set_beacon(struct ath_softc *sc)
break;
case NL80211_IFTYPE_STATION:
ath_beacon_config_sta(sc, cur_conf);
- /*
- * Request a re-configuration of Beacon related timers
- * on the receipt of the first Beacon frame (i.e.,
- * after time sync with the AP).
- */
- sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
break;
default:
ath_dbg(common, ATH_DBG_CONFIG,
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index a8d9009..0fb01b7 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1932,6 +1932,12 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
"Bss Info ASSOC %d, bssid: %pM\n",
bss_conf->aid, common->curbssid);
ath_beacon_config(sc, vif);
+ /*
+ * Request a re-configuration of Beacon related timers
+ * on the receipt of the first Beacon frame (i.e.,
+ * after time sync with the AP).
+ */
+ sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
/* Reset rssi stats */
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
--
1.7.4.4


2011-04-22 17:01:05

by Sam Leffler

[permalink] [raw]
Subject: Re: [PATCH 1/2] ath9k_hw: Fix Tx IQ Calibration hang issue in AR9003 chips

On Fri, Apr 22, 2011 at 2:20 AM, Rajkumar Manoharan
<[email protected]> wrote:
> On AR9003 chips, doing three IQ calibrations will possibly cause chip
> in stuck state. In noisy environment, chip could receive
> a packet during the middle of three calibrations and it causes
> the conflict of HW access and the eventual failure. It also
> causes IQ calibration outliers which results in poor Tx EVM.
> During the issue kernel log was filled with following message
>
> ath: timeout (100000 us) on reg 0xa640: 0x00000001 & 0x00000001 != 0x00000000
> ath: timeout (100000 us) on reg 0xa2c4: 0x00158dd9 & 0x00000001 != 0x00000000
> ath: Unable to reset channel (2412 MHz), reset status -5
> ath: Unable to set channel
>
> Signed-off-by: Rajkumar Manoharan <[email protected]>

Please indicate the specific parts where this issue has been seen. Is
this specific to 9485 or has it been observed on 93xx devices too?

-Sam

2011-04-24 16:02:30

by Rajkumar Manoharan

[permalink] [raw]
Subject: Re: [PATCH 1/2] ath9k_hw: Fix Tx IQ Calibration hang issue in AR9003 chips

On Fri, Apr 22, 2011 at 10:30:59PM +0530, Sam Leffler wrote:
> On Fri, Apr 22, 2011 at 2:20 AM, Rajkumar Manoharan
> <[email protected]> wrote:
> > On AR9003 chips, doing three IQ calibrations will possibly cause chip
> > in stuck state. In noisy environment, chip could receive
> > a packet during the middle of three calibrations and it causes
> > the conflict of HW access and the eventual failure. It also
> > causes IQ calibration outliers which results in poor Tx EVM.
> > During the issue kernel log was filled with following message
> >
> > ath: timeout (100000 us) on reg 0xa640: 0x00000001 & 0x00000001 != 0x00000000
> > ath: timeout (100000 us) on reg 0xa2c4: 0x00158dd9 & 0x00000001 != 0x00000000
> > ath: Unable to reset channel (2412 MHz), reset status -5
> > ath: Unable to set channel
> >
> > Signed-off-by: Rajkumar Manoharan <[email protected]>
>
> Please indicate the specific parts where this issue has been seen. Is
> this specific to 9485 or has it been observed on 93xx devices too?
>
The IQ Cal procedure is after resetting the chip, run IQ cal 3 times
per each cal cycle and find the two closest readings and average of two.
The advantage of running Tx IQ cal more than once is that we can compare
calibration results for the same gain setting over multiple iterations.
Most of the cases the IQ failures were observed after first pass.

For the AR9485 and later chips, Tx IQ Calibration is performed along
with AGC cal. But for pre-AR9485 chips, Tx IQ cal HW has to be separated
from the rest of calibration HW to avoid chip hang. After all calibrations
are done in HW, we can start SW post-processing. By doing this way, we
minimize the SW difference among all chips.

The order of calibration (run IQ cal before other calibration) is also
needed to avoid chip hang for chips before AR9485. This issue was originally
observed with AR9382. Will send v2 patch with updated commit log.

Rajkumar

2011-08-31 22:16:27

by Paul Stewart

[permalink] [raw]
Subject: Re: [PATCH 1/2] ath9k_hw: Fix Tx IQ Calibration hang issue in AR9003 chips

On Fri, Apr 22, 2011 at 2:20 AM, Rajkumar Manoharan
<[email protected]> wrote:
> On AR9003 chips, doing three IQ calibrations will possibly cause chip
> in stuck state. In noisy environment, chip could receive
> a packet during the middle of three calibrations and it causes
> the conflict of HW access and the eventual failure. It also
> causes IQ calibration outliers which results in poor Tx EVM.
[...]
> + ? ? ? /* find min/max mismatch across all calibrated gains */
> + ? ? ? for (i = 0; i < nmeasurement; i++) {
> + ? ? ? ? ? ? ? mp_avg += mp_coeff[i];
> + ? ? ? ? ? ? ? if (mp_coeff[i] > mp_max) {
> + ? ? ? ? ? ? ? ? ? ? ? mp_max = mp_coeff[i];
> + ? ? ? ? ? ? ? ? ? ? ? max_idx = i;
> + ? ? ? ? ? ? ? } else if (mp_coeff[i] < mp_min) {
> + ? ? ? ? ? ? ? ? ? ? ? mp_min = mp_coeff[i];
> + ? ? ? ? ? ? ? ? ? ? ? min_idx = i;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
>
> - ? ? ? if (diff[0] <= diff[1] && diff[0] <= diff[2])
> - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[0] + mp_coeff[1]) / 2;
> - ? ? ? else if (diff[1] <= diff[2])
> - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[1] + mp_coeff[2]) / 2;
> - ? ? ? else
> - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[2] + mp_coeff[0]) / 2;
> + ? ? ? /* find average (exclude max abs value) */
> + ? ? ? for (i = 0; i < nmeasurement; i++) {
> + ? ? ? ? ? ? ? if ((abs(mp_coeff[i]) < abs(mp_max)) ||
> + ? ? ? ? ? ? ? ? ? (abs(mp_coeff[i]) < abs(mp_min)))
Doesn't this potentially throw away more than one potential sample
from your mp_avg sum if more than one value has the same (maximal
absolute) value? You don't account for it below since you divide by
(nmeasurement - 1) so your average can get arbitrarily smaller than
you intended, which may false-trigger your outlier detection.
> + ? ? ? ? ? ? ? ? ? ? ? mp_avg += mp_coeff[i];
> + ? ? ? }
> + ? ? ? mp_avg /= (nmeasurement - 1);
>
> - ? ? ? return true;
> + ? ? ? /* detect outlier */
> + ? ? ? if (abs(mp_max - mp_min) > max_delta) {
> + ? ? ? ? ? ? ? if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg))
> + ? ? ? ? ? ? ? ? ? ? ? outlier_idx = max_idx;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? outlier_idx = min_idx;
> + ? ? ? }
> + ? ? ? mp_coeff[outlier_idx] = mp_avg;
> ?}

2011-09-05 08:16:33

by Rajkumar Manoharan

[permalink] [raw]
Subject: Re: [PATCH 1/2] ath9k_hw: Fix Tx IQ Calibration hang issue in AR9003 chips

On Wed, Aug 31, 2011 at 03:16:22PM -0700, Paul Stewart wrote:
> On Fri, Apr 22, 2011 at 2:20 AM, Rajkumar Manoharan
> <[email protected]> wrote:
> > On AR9003 chips, doing three IQ calibrations will possibly cause chip
> > in stuck state. In noisy environment, chip could receive
> > a packet during the middle of three calibrations and it causes
> > the conflict of HW access and the eventual failure. It also
> > causes IQ calibration outliers which results in poor Tx EVM.
> [...]
> > - ? ? ? if (diff[0] <= diff[1] && diff[0] <= diff[2])
> > - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[0] + mp_coeff[1]) / 2;
> > - ? ? ? else if (diff[1] <= diff[2])
> > - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[1] + mp_coeff[2]) / 2;
> > - ? ? ? else
> > - ? ? ? ? ? ? ? *mp_avg = (mp_coeff[2] + mp_coeff[0]) / 2;
> > + ? ? ? /* find average (exclude max abs value) */
> > + ? ? ? for (i = 0; i < nmeasurement; i++) {
> > + ? ? ? ? ? ? ? if ((abs(mp_coeff[i]) < abs(mp_max)) ||
> > + ? ? ? ? ? ? ? ? ? (abs(mp_coeff[i]) < abs(mp_min)))
> Doesn't this potentially throw away more than one potential sample
> from your mp_avg sum if more than one value has the same (maximal
> absolute) value? You don't account for it below since you divide by
> (nmeasurement - 1) so your average can get arbitrarily smaller than
> you intended, which may false-trigger your outlier detection.
Sorry for the delayed response. And yes it is a bug although it
has minor impact. Based on our Systems team folk's input this bug
itself does not affect the outlier detection because detection
relies on ABS(max-min). It does affect post-processing after outlier
is detected.

It has minor impact because of following facts:
1) Outlier happens very rarely according to the past experiment.
Its frequency should be much less than once every 1000 calibrations.
2) Because of this bug, an undesirable Tx IQ correction value will be assigned
to Tx gain settings where outlier happens. The impacts on overall
system throughput should be minor.

Thanks for your valuable input. Will send the followup patch after
the verification.

--
Rajkumar