/* * Copyright (c) 2010-2011 Atheros Communications Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct coeff { int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int iqc_coeff[2];
};
/* Calibration in progress. */ if (currCal->calState == CAL_RUNNING) { /* Check to see if it has finished. */ if (REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL) returnfalse;
/* * Accumulate cal measures for active chains
*/
cur_caldata->calCollect(ah);
ah->cal_samples++;
if (ah->cal_samples >= cur_caldata->calNumSamples) { unsignedint i, numChains = 0; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (rxchainmask & (1 << i))
numChains++;
}
/* * Process accumulated data
*/
cur_caldata->calPostProc(ah, numChains);
/* Calibration has finished. */
caldata->CalValid |= cur_caldata->calType;
currCal->calState = CAL_DONE; returntrue;
} else { /* * Set-up collection of another sub-sample until we * get desired number
*/
ar9003_hw_setup_calibration(ah, currCal);
}
} elseif (!(caldata->CalValid & cur_caldata->calType)) { /* If current cal is marked invalid in channel, kick it off */
ath9k_hw_reset_calibration(ah, currCal);
}
/* * For given calibration: * 1. Call generic cal routine * 2. When this cal is done (isCalDone) if we have more cals waiting * (eg after reset), mask this to upper layers by not propagating * isCalDone if it is set to TRUE. * Instead, change isCalDone to FALSE and setup the waiting cal(s) * to be run.
*/ if (currCal &&
(currCal->calState == CAL_RUNNING ||
currCal->calState == CAL_WAITING)) {
iscaldone = ar9003_hw_per_calibration(ah, chan,
rxchainmask, currCal); if (iscaldone) {
ah->cal_list_curr = currCal = currCal->calNext;
/* * Do NF cal only at longer intervals. Get the value from * the previous NF cal and update history buffer.
*/ if (longcal && ath9k_hw_getnf(ah, chan)) { /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical * value.
*/
ret = ath9k_hw_loadnf(ah, ah->curchan); if (ret < 0) return ret;
ath_dbg(common, CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i);
}
}
REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0,
AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE);
ath_dbg(common, CALIBRATE, "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n",
(unsigned) (AR_PHY_RX_IQCAL_CORR_B0),
AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE,
REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0));
}
/* * Clear offset and IQ calibration, run AGC cal.
*/
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_OFFSET_CAL);
REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah),
AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
REG_WRITE(ah, AR_PHY_AGC_CONTROL(ah),
REG_READ(ah, AR_PHY_AGC_CONTROL(ah)) | AR_PHY_AGC_CONTROL_CAL);
status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_CAL,
0, AH_WAIT_TIMEOUT); if (!status) {
ath_dbg(common, CALIBRATE, "AGC cal without offset cal failed to complete in 1ms"); returnfalse;
}
/* * Allow only offset calibration and disable the others * (Carrier Leak calibration, TX Filter calibration and * Peak Detector offset calibration).
*/
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_OFFSET_CAL);
REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
AR_PHY_CL_CAL_ENABLE);
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_FLTR_CAL);
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_PKDET_CAL);
status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL(ah),
AR_PHY_AGC_CONTROL_CAL,
0, AH_WAIT_TIMEOUT); if (!status) {
ath_dbg(common, CALIBRATE, "DC offset cal failed to complete in 1ms"); returnfalse;
}
/* * We don't need to check txiqcal_done here since it is always * set for AR9550.
*/
REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah),
AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
chain_idx, iqc_coeff[1]);
returntrue;
}
staticvoid ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL], int nmeasurement, int max_delta)
{ int mp_max = -64, max_idx = 0; int mp_min = 63, min_idx = 0; int mp_avg = 0, i, outlier_idx = 0, mp_count = 0;
/* find min/max mismatch across all calibrated gains */ for (i = 0; i < nmeasurement; i++) { if (mp_coeff[i][0] > mp_max) {
mp_max = mp_coeff[i][0];
max_idx = i;
} elseif (mp_coeff[i][0] < mp_min) {
mp_min = mp_coeff[i][0];
min_idx = i;
}
}
/* find average (exclude max abs value) */ for (i = 0; i < nmeasurement; i++) { if ((abs(mp_coeff[i][0]) < abs(mp_max)) ||
(abs(mp_coeff[i][0]) < abs(mp_min))) {
mp_avg += mp_coeff[i][0];
mp_count++;
}
}
/* * finding mean magnitude/phase if possible, otherwise * just use the last value as the mean
*/ if (mp_count)
mp_avg /= mp_count; else
mp_avg = mp_coeff[nmeasurement - 1][0];
/* Load the average of 2 passes */ for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue;
nmeasurement = REG_READ_FIELD(ah,
AR_PHY_TX_IQCAL_STATUS_B0(ah),
AR_PHY_CALIBRATED_GAINS_0);
if (nmeasurement > MAX_MEASUREMENT)
nmeasurement = MAX_MEASUREMENT;
/* * Skip normal outlier detection for AR9550.
*/ if (!AR_SREV_9550(ah)) { /* detect outlier only if nmeasurement > 1 */ if (nmeasurement > 1) { /* Detect magnitude outlier */
ar9003_hw_detect_outlier(coeff->mag_coeff[i],
nmeasurement,
MAX_MAG_DELTA);
if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START(ah),
AR_PHY_TX_IQCAL_START_DO_CAL, 0,
AH_WAIT_TIMEOUT)) {
ath_dbg(common, CALIBRATE, "Tx IQ Cal is not completed\n"); returnfalse;
} returntrue;
}
staticvoid __ar955x_tx_iq_cal_sort(struct ath_hw *ah, struct coeff *coeff, int i, int nmeasurement)
{ struct ath_common *common = ath9k_hw_common(ah); int im, ix, iy;
for (im = 0; im < nmeasurement; im++) { for (ix = 0; ix < MAXIQCAL - 1; ix++) { for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) { if (coeff->mag_coeff[i][im][iy] <
coeff->mag_coeff[i][im][ix]) {
swap(coeff->mag_coeff[i][im][ix],
coeff->mag_coeff[i][im][iy]);
} if (coeff->phs_coeff[i][im][iy] <
coeff->phs_coeff[i][im][ix]) {
swap(coeff->phs_coeff[i][im][ix],
coeff->phs_coeff[i][im][iy]);
}
}
}
coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2];
coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2];
ath_dbg(common, CALIBRATE, "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n",
i, im,
coeff->mag_coeff[i][im][0],
coeff->phs_coeff[i][im][0]);
}
}
staticbool ar955x_tx_iq_cal_median(struct ath_hw *ah, struct coeff *coeff, int iqcal_idx, int nmeasurement)
{ int i;
if ((iqcal_idx + 1) != MAXIQCAL) returnfalse;
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
__ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement);
}
for (im = 0; im < nmeasurement; im++) {
ath_dbg(common, 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, CALIBRATE, "Tx IQ Cal failed for chain %d\n", i); goto tx_iqcal_fail;
}
if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
coeff.iqc_coeff)) {
ath_dbg(common, CALIBRATE, "Failed in calculation of IQ correction\n"); goto tx_iqcal_fail;
}
if ((IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) ||
!(ah->enabled_cals & TX_IQ_CAL)) goto skip_tx_iqcal;
/* Do Tx IQ Calibration */
REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1(ah),
AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
DELPT);
/* * For AR9485 or later chips, TxIQ cal runs as part of * AGC calibration
*/ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags))
REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah),
AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); else
REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah),
AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
txiqcal_done = run_agc_cal = true;
}
skip_tx_iqcal: if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
ar9003_mci_init_cal_req(ah, &is_reusable);
if (run_rtt_cal && caldata) { if (is_reusable) { if (!ath9k_hw_rfbus_req(ah)) {
ath_err(ath9k_hw_common(ah), "Could not stop baseband\n");
} else {
ar9003_hw_rtt_fill_hist(ah);
if (test_bit(SW_PKDET_DONE, &caldata->cal_flags))
ar9003_hw_rtt_load_hist(ah);
}
ath9k_hw_rfbus_done(ah);
}
ar9003_hw_rtt_disable(ah);
}
/* Revert chainmask to runtime parameters */
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) goto skip_tx_iqcal;
/* Do Tx IQ Calibration */
REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1(ah),
AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
DELPT);
/* * For AR9485 or later chips, TxIQ cal runs as part of * AGC calibration. Specifically, AR9550 in SoC chips.
*/ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah),
AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) {
txiqcal_done = true;
} else {
txiqcal_done = false;
}
run_agc_cal = true;
} else {
sep_iq_cal = true;
run_agc_cal = true;
}
/* * In the SoC family, this will run for AR9300, AR9331 and AR9340.
*/ if (sep_iq_cal) {
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);
}
if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) { if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done)) returnfalse;
}
skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue;
ar9003_hw_manual_peak_cal(ah, i,
IS_CHAN_2GHZ(chan));
}
/* * For non-AR9550 chips, we just trigger AGC calibration * in the HW, poll for completion and then process * the results. * * For AR955x, we run it multiple times and use * median IQ correction.
*/ if (!AR_SREV_9550(ah)) {
status = do_ar9003_agc_cal(ah); if (!status) returnfalse;
if (txiqcal_done)
ar9003_hw_tx_iq_cal_post_proc(ah, 0, false);
} else { if (!txiqcal_done) {
status = do_ar9003_agc_cal(ah); if (!status) returnfalse;
} else { for (i = 0; i < MAXIQCAL; i++) {
status = do_ar9003_agc_cal(ah); if (!status) returnfalse;
ar9003_hw_tx_iq_cal_post_proc(ah, i, false);
}
}
}
}
/* Revert chainmask to runtime parameters */
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.