iPXE
ath9k_ar9003_calib.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010-2011 Atheros Communications Inc.
00003  *
00004  * Modified for iPXE by Scott K Logan <logans@cottsay.net> July 2011
00005  * Original from Linux kernel 3.0.1
00006  *
00007  * Permission to use, copy, modify, and/or distribute this software for any
00008  * purpose with or without fee is hereby granted, provided that the above
00009  * copyright notice and this permission notice appear in all copies.
00010  *
00011  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00012  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00013  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00014  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00015  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00016  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00017  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00018  */
00019 
00020 #include <ipxe/io.h>
00021 
00022 #include "hw.h"
00023 #include "hw-ops.h"
00024 #include "ar9003_phy.h"
00025 
00026 #define MAX_MEASUREMENT 8
00027 #define MAX_MAG_DELTA   11
00028 #define MAX_PHS_DELTA   10
00029 
00030 struct coeff {
00031         int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
00032         int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
00033         int iqc_coeff[2];
00034 };
00035 
00036 enum ar9003_cal_types {
00037         IQ_MISMATCH_CAL = BIT(0),
00038         TEMP_COMP_CAL = BIT(1),
00039 };
00040 
00041 static void ar9003_hw_setup_calibration(struct ath_hw *ah,
00042                                         struct ath9k_cal_list *currCal)
00043 {
00044         /* Select calibration to run */
00045         switch (currCal->calData->calType) {
00046         case IQ_MISMATCH_CAL:
00047                 /*
00048                  * Start calibration with
00049                  * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples
00050                  */
00051                 REG_RMW_FIELD(ah, AR_PHY_TIMING4,
00052                               AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX,
00053                 currCal->calData->calCountMax);
00054                 REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
00055 
00056                 DBG2("ath9k: "
00057                         "starting IQ Mismatch Calibration\n");
00058 
00059                 /* Kick-off cal */
00060                 REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
00061                 break;
00062         case TEMP_COMP_CAL:
00063                 REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
00064                               AR_PHY_65NM_CH0_THERM_LOCAL, 1);
00065                 REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
00066                               AR_PHY_65NM_CH0_THERM_START, 1);
00067 
00068                 DBG2("ath9k: "
00069                         "starting Temperature Compensation Calibration\n");
00070                 break;
00071         }
00072 }
00073 
00074 /*
00075  * Generic calibration routine.
00076  * Recalibrate the lower PHY chips to account for temperature/environment
00077  * changes.
00078  */
00079 static int ar9003_hw_per_calibration(struct ath_hw *ah,
00080                                       struct ath9k_channel *ichan __unused,
00081                                       u8 rxchainmask,
00082                                       struct ath9k_cal_list *currCal)
00083 {
00084         struct ath9k_hw_cal_data *caldata = ah->caldata;
00085         /* Cal is assumed not done until explicitly set below */
00086         int iscaldone = 0;
00087 
00088         /* Calibration in progress. */
00089         if (currCal->calState == CAL_RUNNING) {
00090                 /* Check to see if it has finished. */
00091                 if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) {
00092                         /*
00093                         * Accumulate cal measures for active chains
00094                         */
00095                         currCal->calData->calCollect(ah);
00096                         ah->cal_samples++;
00097 
00098                         if (ah->cal_samples >=
00099                             currCal->calData->calNumSamples) {
00100                                 unsigned int i, numChains = 0;
00101                                 for (i = 0; i < AR9300_MAX_CHAINS; i++) {
00102                                         if (rxchainmask & (1 << i))
00103                                                 numChains++;
00104                                 }
00105 
00106                                 /*
00107                                 * Process accumulated data
00108                                 */
00109                                 currCal->calData->calPostProc(ah, numChains);
00110 
00111                                 /* Calibration has finished. */
00112                                 caldata->CalValid |= currCal->calData->calType;
00113                                 currCal->calState = CAL_DONE;
00114                                 iscaldone = 1;
00115                         } else {
00116                         /*
00117                          * Set-up collection of another sub-sample until we
00118                          * get desired number
00119                          */
00120                         ar9003_hw_setup_calibration(ah, currCal);
00121                         }
00122                 }
00123         } else if (!(caldata->CalValid & currCal->calData->calType)) {
00124                 /* If current cal is marked invalid in channel, kick it off */
00125                 ath9k_hw_reset_calibration(ah, currCal);
00126         }
00127 
00128         return iscaldone;
00129 }
00130 
00131 static int ar9003_hw_calibrate(struct ath_hw *ah,
00132                                 struct ath9k_channel *chan,
00133                                 u8 rxchainmask,
00134                                 int longcal)
00135 {
00136         int iscaldone = 1;
00137         struct ath9k_cal_list *currCal = ah->cal_list_curr;
00138 
00139         /*
00140          * For given calibration:
00141          * 1. Call generic cal routine
00142          * 2. When this cal is done (isCalDone) if we have more cals waiting
00143          *    (eg after reset), mask this to upper layers by not propagating
00144          *    isCalDone if it is set to TRUE.
00145          *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
00146          *    to be run.
00147          */
00148         if (currCal &&
00149             (currCal->calState == CAL_RUNNING ||
00150              currCal->calState == CAL_WAITING)) {
00151                 iscaldone = ar9003_hw_per_calibration(ah, chan,
00152                                                       rxchainmask, currCal);
00153                 if (iscaldone) {
00154                         ah->cal_list_curr = currCal = currCal->calNext;
00155 
00156                         if (currCal->calState == CAL_WAITING) {
00157                                 iscaldone = 0;
00158                                 ath9k_hw_reset_calibration(ah, currCal);
00159                         }
00160                 }
00161         }
00162 
00163         /* Do NF cal only at longer intervals */
00164         if (longcal) {
00165                 /*
00166                  * Get the value from the previous NF cal and update
00167                  * history buffer.
00168                  */
00169                 ath9k_hw_getnf(ah, chan);
00170 
00171                 /*
00172                  * Load the NF from history buffer of the current channel.
00173                  * NF is slow time-variant, so it is OK to use a historical
00174                  * value.
00175                  */
00176                 ath9k_hw_loadnf(ah, ah->curchan);
00177 
00178                 /* start NF calibration, without updating BB NF register */
00179                 ath9k_hw_start_nfcal(ah, 0);
00180         }
00181 
00182         return iscaldone;
00183 }
00184 
00185 static void ar9003_hw_iqcal_collect(struct ath_hw *ah)
00186 {
00187         int i;
00188 
00189         /* Accumulate IQ cal measures for active chains */
00190         for (i = 0; i < AR5416_MAX_CHAINS; i++) {
00191                 if (ah->txchainmask & BIT(i)) {
00192                         ah->totalPowerMeasI[i] +=
00193                                 REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
00194                         ah->totalPowerMeasQ[i] +=
00195                                 REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
00196                         ah->totalIqCorrMeas[i] +=
00197                                 (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
00198                         DBG2("ath9k: "
00199                                 "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
00200                                 ah->cal_samples, i, ah->totalPowerMeasI[i],
00201                                 ah->totalPowerMeasQ[i],
00202                                 ah->totalIqCorrMeas[i]);
00203                 }
00204         }
00205 }
00206 
00207 static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
00208 {
00209         u32 powerMeasQ, powerMeasI, iqCorrMeas;
00210         u32 qCoffDenom, iCoffDenom;
00211         int32_t qCoff, iCoff;
00212         int iqCorrNeg, i;
00213         static const uint32_t offset_array[3] = {
00214                 AR_PHY_RX_IQCAL_CORR_B0,
00215                 AR_PHY_RX_IQCAL_CORR_B1,
00216                 AR_PHY_RX_IQCAL_CORR_B2,
00217         };
00218 
00219         for (i = 0; i < numChains; i++) {
00220                 powerMeasI = ah->totalPowerMeasI[i];
00221                 powerMeasQ = ah->totalPowerMeasQ[i];
00222                 iqCorrMeas = ah->totalIqCorrMeas[i];
00223 
00224                 DBG2("ath9k: "
00225                         "Starting IQ Cal and Correction for Chain %d\n",
00226                         i);
00227 
00228                 DBG2("ath9k: "
00229                         "Orignal: Chn %diq_corr_meas = 0x%08x\n",
00230                         i, ah->totalIqCorrMeas[i]);
00231 
00232                 iqCorrNeg = 0;
00233 
00234                 if (iqCorrMeas > 0x80000000) {
00235                         iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
00236                         iqCorrNeg = 1;
00237                 }
00238 
00239                 DBG2("ath9k: "
00240                         "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
00241                 DBG2("ath9k: "
00242                         "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
00243                 DBG2("ath9k: iqCorrNeg is 0x%08x\n",
00244                         iqCorrNeg);
00245 
00246                 iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256;
00247                 qCoffDenom = powerMeasQ / 64;
00248 
00249                 if ((iCoffDenom != 0) && (qCoffDenom != 0)) {
00250                         iCoff = iqCorrMeas / iCoffDenom;
00251                         qCoff = powerMeasI / qCoffDenom - 64;
00252                         DBG2("ath9k: "
00253                                 "Chn %d iCoff = 0x%08x\n", i, iCoff);
00254                         DBG2("ath9k: "
00255                                 "Chn %d qCoff = 0x%08x\n", i, qCoff);
00256 
00257                         /* Force bounds on iCoff */
00258                         if (iCoff >= 63)
00259                                 iCoff = 63;
00260                         else if (iCoff <= -63)
00261                                 iCoff = -63;
00262 
00263                         /* Negate iCoff if iqCorrNeg == 0 */
00264                         if (iqCorrNeg == 0x0)
00265                                 iCoff = -iCoff;
00266 
00267                         /* Force bounds on qCoff */
00268                         if (qCoff >= 63)
00269                                 qCoff = 63;
00270                         else if (qCoff <= -63)
00271                                 qCoff = -63;
00272 
00273                         iCoff = iCoff & 0x7f;
00274                         qCoff = qCoff & 0x7f;
00275 
00276                         DBG2("ath9k: "
00277                                 "Chn %d : iCoff = 0x%x  qCoff = 0x%x\n",
00278                                 i, iCoff, qCoff);
00279                         DBG2("ath9k: "
00280                                 "Register offset (0x%04x) before update = 0x%x\n",
00281                                 offset_array[i],
00282                                 REG_READ(ah, offset_array[i]));
00283 
00284                         REG_RMW_FIELD(ah, offset_array[i],
00285                                       AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
00286                                       iCoff);
00287                         REG_RMW_FIELD(ah, offset_array[i],
00288                                       AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
00289                                       qCoff);
00290                         DBG2("ath9k: "
00291                                 "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n",
00292                                 offset_array[i],
00293                                 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
00294                                 REG_READ(ah, offset_array[i]));
00295                         DBG2("ath9k: "
00296                                 "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n",
00297                                 offset_array[i],
00298                                 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
00299                                 REG_READ(ah, offset_array[i]));
00300 
00301                         DBG2("ath9k: "
00302                                 "IQ Cal and Correction done for Chain %d\n", i);
00303                 }
00304         }
00305 
00306         REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0,
00307                     AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE);
00308         DBG2("ath9k: "
00309                 "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n",
00310                 (unsigned) (AR_PHY_RX_IQCAL_CORR_B0),
00311                 AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE,
00312                 REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0));
00313 }
00314 
00315 static const struct ath9k_percal_data iq_cal_single_sample = {
00316         IQ_MISMATCH_CAL,
00317         MIN_CAL_SAMPLES,
00318         PER_MAX_LOG_COUNT,
00319         ar9003_hw_iqcal_collect,
00320         ar9003_hw_iqcalibrate
00321 };
00322 
00323 static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
00324 {
00325         ah->iq_caldata.calData = &iq_cal_single_sample;
00326 }
00327 
00328 /*
00329  * solve 4x4 linear equation used in loopback iq cal.
00330  */
00331 static int ar9003_hw_solve_iq_cal(struct ath_hw *ah __unused,
00332                                    s32 sin_2phi_1,
00333                                    s32 cos_2phi_1,
00334                                    s32 sin_2phi_2,
00335                                    s32 cos_2phi_2,
00336                                    s32 mag_a0_d0,
00337                                    s32 phs_a0_d0,
00338                                    s32 mag_a1_d0,
00339                                    s32 phs_a1_d0,
00340                                    s32 solved_eq[])
00341 {
00342         s32 f1 = cos_2phi_1 - cos_2phi_2,
00343             f3 = sin_2phi_1 - sin_2phi_2,
00344             f2;
00345         s32 mag_tx, phs_tx, mag_rx, phs_rx;
00346         const s32 result_shift = 1 << 15;
00347 
00348         f2 = (f1 * f1 + f3 * f3) / result_shift;
00349 
00350         if (!f2) {
00351                 DBG("ath9k: Divide by 0\n");
00352                 return 0;
00353         }
00354 
00355         /* mag mismatch, tx */
00356         mag_tx = f1 * (mag_a0_d0  - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0);
00357         /* phs mismatch, tx */
00358         phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0);
00359 
00360         mag_tx = (mag_tx / f2);
00361         phs_tx = (phs_tx / f2);
00362 
00363         /* mag mismatch, rx */
00364         mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) /
00365                  result_shift;
00366         /* phs mismatch, rx */
00367         phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) /
00368                  result_shift;
00369 
00370         solved_eq[0] = mag_tx;
00371         solved_eq[1] = phs_tx;
00372         solved_eq[2] = mag_rx;
00373         solved_eq[3] = phs_rx;
00374 
00375         return 1;
00376 }
00377 
00378 static s32 ar9003_hw_find_mag_approx(struct ath_hw *ah __unused, s32 in_re, s32 in_im)
00379 {
00380         s32 abs_i = abs(in_re),
00381             abs_q = abs(in_im),
00382             max_abs, min_abs;
00383 
00384         if (abs_i > abs_q) {
00385                 max_abs = abs_i;
00386                 min_abs = abs_q;
00387         } else {
00388                 max_abs = abs_q;
00389                 min_abs = abs_i;
00390         }
00391 
00392         return max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4);
00393 }
00394 
00395 #define DELPT 32
00396 
00397 static int ar9003_hw_calc_iq_corr(struct ath_hw *ah,
00398                                    s32 chain_idx,
00399                                    const s32 iq_res[],
00400                                    s32 iqc_coeff[])
00401 {
00402         s32 i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0,
00403             i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1,
00404             i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0,
00405             i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1;
00406         s32 mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1,
00407             phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1,
00408             sin_2phi_1, cos_2phi_1,
00409             sin_2phi_2, cos_2phi_2;
00410         s32 mag_tx, phs_tx, mag_rx, phs_rx;
00411         s32 solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx,
00412             q_q_coff, q_i_coff;
00413         const s32 res_scale = 1 << 15;
00414         const s32 delpt_shift = 1 << 8;
00415         s32 mag1, mag2;
00416 
00417         i2_m_q2_a0_d0 = iq_res[0] & 0xfff;
00418         i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff;
00419         iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8);
00420 
00421         if (i2_m_q2_a0_d0 > 0x800)
00422                 i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1);
00423 
00424         if (i2_p_q2_a0_d0 > 0x800)
00425                 i2_p_q2_a0_d0 = -((0xfff - i2_p_q2_a0_d0) + 1);
00426 
00427         if (iq_corr_a0_d0 > 0x800)
00428                 iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1);
00429 
00430         i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff;
00431         i2_p_q2_a0_d1 = (iq_res[2] & 0xfff);
00432         iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff;
00433 
00434         if (i2_m_q2_a0_d1 > 0x800)
00435                 i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1);
00436 
00437         if (i2_p_q2_a0_d1 > 0x800)
00438                 i2_p_q2_a0_d1 = -((0xfff - i2_p_q2_a0_d1) + 1);
00439 
00440         if (iq_corr_a0_d1 > 0x800)
00441                 iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1);
00442 
00443         i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8);
00444         i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff;
00445         iq_corr_a1_d0 = iq_res[4] & 0xfff;
00446 
00447         if (i2_m_q2_a1_d0 > 0x800)
00448                 i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1);
00449 
00450         if (i2_p_q2_a1_d0 > 0x800)
00451                 i2_p_q2_a1_d0 = -((0xfff - i2_p_q2_a1_d0) + 1);
00452 
00453         if (iq_corr_a1_d0 > 0x800)
00454                 iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1);
00455 
00456         i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff;
00457         i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8);
00458         iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff;
00459 
00460         if (i2_m_q2_a1_d1 > 0x800)
00461                 i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1);
00462 
00463         if (i2_p_q2_a1_d1 > 0x800)
00464                 i2_p_q2_a1_d1 = -((0xfff - i2_p_q2_a1_d1) + 1);
00465 
00466         if (iq_corr_a1_d1 > 0x800)
00467                 iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1);
00468 
00469         if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) ||
00470             (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) {
00471                 DBG("ath9k: "
00472                         "Divide by 0:\n"
00473                         "a0_d0=%d\n"
00474                         "a0_d1=%d\n"
00475                         "a2_d0=%d\n"
00476                         "a1_d1=%d\n",
00477                         i2_p_q2_a0_d0, i2_p_q2_a0_d1,
00478                         i2_p_q2_a1_d0, i2_p_q2_a1_d1);
00479                 return 0;
00480         }
00481 
00482         mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0;
00483         phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0;
00484 
00485         mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1;
00486         phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1;
00487 
00488         mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0;
00489         phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0;
00490 
00491         mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1;
00492         phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1;
00493 
00494         /* w/o analog phase shift */
00495         sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT);
00496         /* w/o analog phase shift */
00497         cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT);
00498         /* w/  analog phase shift */
00499         sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT);
00500         /* w/  analog phase shift */
00501         cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT);
00502 
00503         /*
00504          * force sin^2 + cos^2 = 1;
00505          * find magnitude by approximation
00506          */
00507         mag1 = ar9003_hw_find_mag_approx(ah, cos_2phi_1, sin_2phi_1);
00508         mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2);
00509 
00510         if ((mag1 == 0) || (mag2 == 0)) {
00511                 DBG("ath9k: "
00512                         "Divide by 0: mag1=%d, mag2=%d\n",
00513                         mag1, mag2);
00514                 return 0;
00515         }
00516 
00517         /* normalization sin and cos by mag */
00518         sin_2phi_1 = (sin_2phi_1 * res_scale / mag1);
00519         cos_2phi_1 = (cos_2phi_1 * res_scale / mag1);
00520         sin_2phi_2 = (sin_2phi_2 * res_scale / mag2);
00521         cos_2phi_2 = (cos_2phi_2 * res_scale / mag2);
00522 
00523         /* calculate IQ mismatch */
00524         if (!ar9003_hw_solve_iq_cal(ah,
00525                              sin_2phi_1, cos_2phi_1,
00526                              sin_2phi_2, cos_2phi_2,
00527                              mag_a0_d0, phs_a0_d0,
00528                              mag_a1_d0,
00529                              phs_a1_d0, solved_eq)) {
00530                 DBG("ath9k: "
00531                         "Call to ar9003_hw_solve_iq_cal() failed.\n");
00532                 return 0;
00533         }
00534 
00535         mag_tx = solved_eq[0];
00536         phs_tx = solved_eq[1];
00537         mag_rx = solved_eq[2];
00538         phs_rx = solved_eq[3];
00539 
00540         DBG2("ath9k: "
00541                 "chain %d: mag mismatch=%d phase mismatch=%d\n",
00542                 chain_idx, mag_tx/res_scale, phs_tx/res_scale);
00543 
00544         if (res_scale == mag_tx) {
00545                 DBG("ath9k: "
00546                         "Divide by 0: mag_tx=%d, res_scale=%d\n",
00547                         mag_tx, res_scale);
00548                 return 0;
00549         }
00550 
00551         /* calculate and quantize Tx IQ correction factor */
00552         mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx);
00553         phs_corr_tx = -phs_tx;
00554 
00555         q_q_coff = (mag_corr_tx * 128 / res_scale);
00556         q_i_coff = (phs_corr_tx * 256 / res_scale);
00557 
00558         DBG2("ath9k: "
00559                 "tx chain %d: mag corr=%d  phase corr=%d\n",
00560                 chain_idx, q_q_coff, q_i_coff);
00561 
00562         if (q_i_coff < -63)
00563                 q_i_coff = -63;
00564         if (q_i_coff > 63)
00565                 q_i_coff = 63;
00566         if (q_q_coff < -63)
00567                 q_q_coff = -63;
00568         if (q_q_coff > 63)
00569                 q_q_coff = 63;
00570 
00571         iqc_coeff[0] = (q_q_coff * 128) + q_i_coff;
00572 
00573         DBG2("ath9k: "
00574                 "tx chain %d: iq corr coeff=%x\n",
00575                 chain_idx, iqc_coeff[0]);
00576 
00577         if (-mag_rx == res_scale) {
00578                 DBG("ath9k: "
00579                         "Divide by 0: mag_rx=%d, res_scale=%d\n",
00580                         mag_rx, res_scale);
00581                 return 0;
00582         }
00583 
00584         /* calculate and quantize Rx IQ correction factors */
00585         mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx);
00586         phs_corr_rx = -phs_rx;
00587 
00588         q_q_coff = (mag_corr_rx * 128 / res_scale);
00589         q_i_coff = (phs_corr_rx * 256 / res_scale);
00590 
00591         DBG("ath9k: "
00592                 "rx chain %d: mag corr=%d  phase corr=%d\n",
00593                 chain_idx, q_q_coff, q_i_coff);
00594 
00595         if (q_i_coff < -63)
00596                 q_i_coff = -63;
00597         if (q_i_coff > 63)
00598                 q_i_coff = 63;
00599         if (q_q_coff < -63)
00600                 q_q_coff = -63;
00601         if (q_q_coff > 63)
00602                 q_q_coff = 63;
00603 
00604         iqc_coeff[1] = (q_q_coff * 128) + q_i_coff;
00605 
00606         DBG2("ath9k: "
00607                 "rx chain %d: iq corr coeff=%x\n",
00608                 chain_idx, iqc_coeff[1]);
00609 
00610         return 1;
00611 }
00612 
00613 static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
00614                                      int max_delta)
00615 {
00616         int mp_max = -64, max_idx = 0;
00617         int mp_min = 63, min_idx = 0;
00618         int mp_avg = 0, i, outlier_idx = 0;
00619 
00620         /* find min/max mismatch across all calibrated gains */
00621         for (i = 0; i < nmeasurement; i++) {
00622                 mp_avg += mp_coeff[i];
00623                 if (mp_coeff[i] > mp_max) {
00624                         mp_max = mp_coeff[i];
00625                         max_idx = i;
00626                 } else if (mp_coeff[i] < mp_min) {
00627                         mp_min = mp_coeff[i];
00628                         min_idx = i;
00629                 }
00630         }
00631 
00632         /* find average (exclude max abs value) */
00633         for (i = 0; i < nmeasurement; i++) {
00634                 if ((abs(mp_coeff[i]) < abs(mp_max)) ||
00635                     (abs(mp_coeff[i]) < abs(mp_min)))
00636                         mp_avg += mp_coeff[i];
00637         }
00638         mp_avg /= (nmeasurement - 1);
00639 
00640         /* detect outlier */
00641         if (abs(mp_max - mp_min) > max_delta) {
00642                 if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg))
00643                         outlier_idx = max_idx;
00644                 else
00645                         outlier_idx = min_idx;
00646         }
00647         mp_coeff[outlier_idx] = mp_avg;
00648 }
00649 
00650 static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
00651                                                  u8 num_chains,
00652                                                  struct coeff *coeff)
00653 {
00654         int i, im, nmeasurement;
00655         u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
00656 
00657         memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
00658         for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
00659                 tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] =
00660                                         AR_PHY_TX_IQCAL_CORR_COEFF_B0(i);
00661                 if (!AR_SREV_9485(ah)) {
00662                         tx_corr_coeff[i * 2][1] =
00663                         tx_corr_coeff[(i * 2) + 1][1] =
00664                                         AR_PHY_TX_IQCAL_CORR_COEFF_B1(i);
00665 
00666                         tx_corr_coeff[i * 2][2] =
00667                         tx_corr_coeff[(i * 2) + 1][2] =
00668                                         AR_PHY_TX_IQCAL_CORR_COEFF_B2(i);
00669                 }
00670         }
00671 
00672         /* Load the average of 2 passes */
00673         for (i = 0; i < num_chains; i++) {
00674                 nmeasurement = REG_READ_FIELD(ah,
00675                                 AR_PHY_TX_IQCAL_STATUS_B0,
00676                                 AR_PHY_CALIBRATED_GAINS_0);
00677 
00678                 if (nmeasurement > MAX_MEASUREMENT)
00679                         nmeasurement = MAX_MEASUREMENT;
00680 
00681                 /* detect outlier only if nmeasurement > 1 */
00682                 if (nmeasurement > 1) {
00683                         /* Detect magnitude outlier */
00684                         ar9003_hw_detect_outlier(coeff->mag_coeff[i],
00685                                         nmeasurement, MAX_MAG_DELTA);
00686 
00687                         /* Detect phase outlier */
00688                         ar9003_hw_detect_outlier(coeff->phs_coeff[i],
00689                                         nmeasurement, MAX_PHS_DELTA);
00690                 }
00691 
00692                 for (im = 0; im < nmeasurement; im++) {
00693 
00694                         coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) |
00695                                 ((coeff->phs_coeff[i][im] & 0x7f) << 7);
00696 
00697                         if ((im % 2) == 0)
00698                                 REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
00699                                         AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
00700                                         coeff->iqc_coeff[0]);
00701                         else
00702                                 REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
00703                                         AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
00704                                         coeff->iqc_coeff[0]);
00705                 }
00706         }
00707 
00708         REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
00709                       AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
00710         REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
00711                       AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
00712 
00713         return;
00714 
00715 }
00716 
00717 static int ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
00718 {
00719         u8 tx_gain_forced;
00720 
00721         tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
00722                                         AR_PHY_TXGAIN_FORCE);
00723         if (tx_gain_forced)
00724                 REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
00725                               AR_PHY_TXGAIN_FORCE, 0);
00726 
00727         REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
00728                       AR_PHY_TX_IQCAL_START_DO_CAL, 1);
00729 
00730         if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
00731                         AR_PHY_TX_IQCAL_START_DO_CAL, 0,
00732                         AH_WAIT_TIMEOUT)) {
00733                 DBG2("ath9k: "
00734                         "Tx IQ Cal is not completed.\n");
00735                 return 0;
00736         }
00737         return 1;
00738 }
00739 
00740 static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
00741 {
00742         const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
00743                 AR_PHY_TX_IQCAL_STATUS_B0,
00744                 AR_PHY_TX_IQCAL_STATUS_B1,
00745                 AR_PHY_TX_IQCAL_STATUS_B2,
00746         };
00747         const uint32_t chan_info_tab[] = {
00748                 AR_PHY_CHAN_INFO_TAB_0,
00749                 AR_PHY_CHAN_INFO_TAB_1,
00750                 AR_PHY_CHAN_INFO_TAB_2,
00751         };
00752         struct coeff coeff;
00753         s32 iq_res[6];
00754         u8 num_chains = 0;
00755         int i, im, j;
00756         int nmeasurement;
00757 
00758         for (i = 0; i < AR9300_MAX_CHAINS; i++) {
00759                 if (ah->txchainmask & (1 << i))
00760                         num_chains++;
00761         }
00762 
00763         for (i = 0; i < num_chains; i++) {
00764                 nmeasurement = REG_READ_FIELD(ah,
00765                                 AR_PHY_TX_IQCAL_STATUS_B0,
00766                                 AR_PHY_CALIBRATED_GAINS_0);
00767                 if (nmeasurement > MAX_MEASUREMENT)
00768                         nmeasurement = MAX_MEASUREMENT;
00769 
00770                 for (im = 0; im < nmeasurement; im++) {
00771                         DBG2("ath9k: "
00772                                 "Doing Tx IQ Cal for chain %d.\n", i);
00773 
00774                         if (REG_READ(ah, txiqcal_status[i]) &
00775                                         AR_PHY_TX_IQCAL_STATUS_FAILED) {
00776                                 DBG("ath9k: "
00777                                         "Tx IQ Cal failed for chain %d.\n", i);
00778                                 goto tx_iqcal_fail;
00779                         }
00780 
00781                         for (j = 0; j < 3; j++) {
00782                                 u32 idx = 2 * j, offset = 4 * (3 * im + j);
00783 
00784                                 REG_RMW_FIELD(ah,
00785                                                 AR_PHY_CHAN_INFO_MEMORY,
00786                                                 AR_PHY_CHAN_INFO_TAB_S2_READ,
00787                                                 0);
00788 
00789                                 /* 32 bits */
00790                                 iq_res[idx] = REG_READ(ah,
00791                                                 chan_info_tab[i] +
00792                                                 offset);
00793 
00794                                 REG_RMW_FIELD(ah,
00795                                                 AR_PHY_CHAN_INFO_MEMORY,
00796                                                 AR_PHY_CHAN_INFO_TAB_S2_READ,
00797                                                 1);
00798 
00799                                 /* 16 bits */
00800                                 iq_res[idx + 1] = 0xffff & REG_READ(ah,
00801                                                 chan_info_tab[i] + offset);
00802 
00803                                 DBG2("ath9k: "
00804                                         "IQ RES[%d]=0x%x"
00805                                         "IQ_RES[%d]=0x%x\n",
00806                                         idx, iq_res[idx], idx + 1,
00807                                         iq_res[idx + 1]);
00808                         }
00809 
00810                         if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
00811                                                 coeff.iqc_coeff)) {
00812                                 DBG("ath9k: "
00813                                         "Failed in calculation of \
00814                                         IQ correction.\n");
00815                                 goto tx_iqcal_fail;
00816                         }
00817 
00818                         coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f;
00819                         coeff.phs_coeff[i][im] =
00820                                 (coeff.iqc_coeff[0] >> 7) & 0x7f;
00821 
00822                         if (coeff.mag_coeff[i][im] > 63)
00823                                 coeff.mag_coeff[i][im] -= 128;
00824                         if (coeff.phs_coeff[i][im] > 63)
00825                                 coeff.phs_coeff[i][im] -= 128;
00826                 }
00827         }
00828         ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, &coeff);
00829 
00830         return;
00831 
00832 tx_iqcal_fail:
00833         DBG("ath9k: Tx IQ Cal failed\n");
00834         return;
00835 }
00836 static int ar9003_hw_init_cal(struct ath_hw *ah,
00837                                struct ath9k_channel *chan __unused)
00838 {
00839         struct ath9k_hw_capabilities *pCap = &ah->caps;
00840         int val;
00841         int txiqcal_done = 0;
00842 
00843         val = REG_READ(ah, AR_ENT_OTP);
00844         DBG2("ath9k: ath9k: AR_ENT_OTP 0x%x\n", val);
00845 
00846         /* Configure rx/tx chains before running AGC/TxiQ cals */
00847         if (val & AR_ENT_OTP_CHAIN2_DISABLE)
00848                 ar9003_hw_set_chain_masks(ah, 0x3, 0x3);
00849         else
00850                 ar9003_hw_set_chain_masks(ah, pCap->rx_chainmask,
00851                                           pCap->tx_chainmask);
00852 
00853         /* Do Tx IQ Calibration */
00854         REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
00855                       AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
00856                       DELPT);
00857 
00858         /*
00859          * For AR9485 or later chips, TxIQ cal runs as part of
00860          * AGC calibration
00861          */
00862         if (AR_SREV_9485_OR_LATER(ah))
00863                 txiqcal_done = 1;
00864         else {
00865                 txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
00866                 REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
00867                 udelay(5);
00868                 REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
00869         }
00870 
00871         /* Calibrate the AGC */
00872         REG_WRITE(ah, AR_PHY_AGC_CONTROL,
00873                   REG_READ(ah, AR_PHY_AGC_CONTROL) |
00874                   AR_PHY_AGC_CONTROL_CAL);
00875 
00876         /* Poll for offset calibration complete */
00877         if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
00878                            0, AH_WAIT_TIMEOUT)) {
00879                 DBG("ath9k: "
00880                         "offset calibration failed to complete in 1ms; noisy environment?\n");
00881                 return 0;
00882         }
00883 
00884         if (txiqcal_done)
00885                 ar9003_hw_tx_iq_cal_post_proc(ah);
00886 
00887         /* Revert chainmasks to their original values before NF cal */
00888         ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
00889 
00890         ath9k_hw_start_nfcal(ah, 1);
00891 
00892         /* Initialize list pointers */
00893         ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
00894         ah->supp_cals = IQ_MISMATCH_CAL;
00895 
00896         if (ah->supp_cals & IQ_MISMATCH_CAL) {
00897                 INIT_CAL(&ah->iq_caldata);
00898                 INSERT_CAL(ah, &ah->iq_caldata);
00899                 DBG2("ath9k: "
00900                         "enabling IQ Calibration.\n");
00901         }
00902 
00903         if (ah->supp_cals & TEMP_COMP_CAL) {
00904                 INIT_CAL(&ah->tempCompCalData);
00905                 INSERT_CAL(ah, &ah->tempCompCalData);
00906                 DBG2("ath9k: "
00907                         "enabling Temperature Compensation Calibration.\n");
00908         }
00909 
00910         /* Initialize current pointer to first element in list */
00911         ah->cal_list_curr = ah->cal_list;
00912 
00913         if (ah->cal_list_curr)
00914                 ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
00915 
00916         if (ah->caldata)
00917                 ah->caldata->CalValid = 0;
00918 
00919         return 1;
00920 }
00921 
00922 void ar9003_hw_attach_calib_ops(struct ath_hw *ah)
00923 {
00924         struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
00925         struct ath_hw_ops *ops = ath9k_hw_ops(ah);
00926 
00927         priv_ops->init_cal_settings = ar9003_hw_init_cal_settings;
00928         priv_ops->init_cal = ar9003_hw_init_cal;
00929         priv_ops->setup_calibration = ar9003_hw_setup_calibration;
00930 
00931         ops->calibrate = ar9003_hw_calibrate;
00932 }