1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (c) 2010-2011 Atheros Communications Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission to use, copy, modify, and/or distribute this software for any
5*4882a593Smuzhiyun * purpose with or without fee is hereby granted, provided that the above
6*4882a593Smuzhiyun * copyright notice and this permission notice appear in all copies.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*4882a593Smuzhiyun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*4882a593Smuzhiyun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*4882a593Smuzhiyun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*4882a593Smuzhiyun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*4882a593Smuzhiyun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*4882a593Smuzhiyun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "hw.h"
18*4882a593Smuzhiyun #include "hw-ops.h"
19*4882a593Smuzhiyun #include "ar9003_phy.h"
20*4882a593Smuzhiyun #include "ar9003_rtt.h"
21*4882a593Smuzhiyun #include "ar9003_mci.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT
24*4882a593Smuzhiyun #define MAX_MAG_DELTA 11
25*4882a593Smuzhiyun #define MAX_PHS_DELTA 10
26*4882a593Smuzhiyun #define MAXIQCAL 3
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun struct coeff {
29*4882a593Smuzhiyun int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
30*4882a593Smuzhiyun int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
31*4882a593Smuzhiyun int iqc_coeff[2];
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun enum ar9003_cal_types {
35*4882a593Smuzhiyun IQ_MISMATCH_CAL = BIT(0),
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
ar9003_hw_setup_calibration(struct ath_hw * ah,struct ath9k_cal_list * currCal)38*4882a593Smuzhiyun static void ar9003_hw_setup_calibration(struct ath_hw *ah,
39*4882a593Smuzhiyun struct ath9k_cal_list *currCal)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* Select calibration to run */
44*4882a593Smuzhiyun switch (currCal->calData->calType) {
45*4882a593Smuzhiyun case IQ_MISMATCH_CAL:
46*4882a593Smuzhiyun /*
47*4882a593Smuzhiyun * Start calibration with
48*4882a593Smuzhiyun * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TIMING4,
51*4882a593Smuzhiyun AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX,
52*4882a593Smuzhiyun currCal->calData->calCountMax);
53*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
56*4882a593Smuzhiyun "starting IQ Mismatch Calibration\n");
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* Kick-off cal */
59*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
60*4882a593Smuzhiyun break;
61*4882a593Smuzhiyun default:
62*4882a593Smuzhiyun ath_err(common, "Invalid calibration type\n");
63*4882a593Smuzhiyun break;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun * Generic calibration routine.
69*4882a593Smuzhiyun * Recalibrate the lower PHY chips to account for temperature/environment
70*4882a593Smuzhiyun * changes.
71*4882a593Smuzhiyun */
ar9003_hw_per_calibration(struct ath_hw * ah,struct ath9k_channel * ichan,u8 rxchainmask,struct ath9k_cal_list * currCal)72*4882a593Smuzhiyun static bool ar9003_hw_per_calibration(struct ath_hw *ah,
73*4882a593Smuzhiyun struct ath9k_channel *ichan,
74*4882a593Smuzhiyun u8 rxchainmask,
75*4882a593Smuzhiyun struct ath9k_cal_list *currCal)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
78*4882a593Smuzhiyun const struct ath9k_percal_data *cur_caldata = currCal->calData;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /* Calibration in progress. */
81*4882a593Smuzhiyun if (currCal->calState == CAL_RUNNING) {
82*4882a593Smuzhiyun /* Check to see if it has finished. */
83*4882a593Smuzhiyun if (REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)
84*4882a593Smuzhiyun return false;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun /*
87*4882a593Smuzhiyun * Accumulate cal measures for active chains
88*4882a593Smuzhiyun */
89*4882a593Smuzhiyun cur_caldata->calCollect(ah);
90*4882a593Smuzhiyun ah->cal_samples++;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun if (ah->cal_samples >= cur_caldata->calNumSamples) {
93*4882a593Smuzhiyun unsigned int i, numChains = 0;
94*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
95*4882a593Smuzhiyun if (rxchainmask & (1 << i))
96*4882a593Smuzhiyun numChains++;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun /*
100*4882a593Smuzhiyun * Process accumulated data
101*4882a593Smuzhiyun */
102*4882a593Smuzhiyun cur_caldata->calPostProc(ah, numChains);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /* Calibration has finished. */
105*4882a593Smuzhiyun caldata->CalValid |= cur_caldata->calType;
106*4882a593Smuzhiyun currCal->calState = CAL_DONE;
107*4882a593Smuzhiyun return true;
108*4882a593Smuzhiyun } else {
109*4882a593Smuzhiyun /*
110*4882a593Smuzhiyun * Set-up collection of another sub-sample until we
111*4882a593Smuzhiyun * get desired number
112*4882a593Smuzhiyun */
113*4882a593Smuzhiyun ar9003_hw_setup_calibration(ah, currCal);
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun } else if (!(caldata->CalValid & cur_caldata->calType)) {
116*4882a593Smuzhiyun /* If current cal is marked invalid in channel, kick it off */
117*4882a593Smuzhiyun ath9k_hw_reset_calibration(ah, currCal);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return false;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
ar9003_hw_calibrate(struct ath_hw * ah,struct ath9k_channel * chan,u8 rxchainmask,bool longcal)123*4882a593Smuzhiyun static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
124*4882a593Smuzhiyun u8 rxchainmask, bool longcal)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun bool iscaldone = true;
127*4882a593Smuzhiyun struct ath9k_cal_list *currCal = ah->cal_list_curr;
128*4882a593Smuzhiyun int ret;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /*
131*4882a593Smuzhiyun * For given calibration:
132*4882a593Smuzhiyun * 1. Call generic cal routine
133*4882a593Smuzhiyun * 2. When this cal is done (isCalDone) if we have more cals waiting
134*4882a593Smuzhiyun * (eg after reset), mask this to upper layers by not propagating
135*4882a593Smuzhiyun * isCalDone if it is set to TRUE.
136*4882a593Smuzhiyun * Instead, change isCalDone to FALSE and setup the waiting cal(s)
137*4882a593Smuzhiyun * to be run.
138*4882a593Smuzhiyun */
139*4882a593Smuzhiyun if (currCal &&
140*4882a593Smuzhiyun (currCal->calState == CAL_RUNNING ||
141*4882a593Smuzhiyun currCal->calState == CAL_WAITING)) {
142*4882a593Smuzhiyun iscaldone = ar9003_hw_per_calibration(ah, chan,
143*4882a593Smuzhiyun rxchainmask, currCal);
144*4882a593Smuzhiyun if (iscaldone) {
145*4882a593Smuzhiyun ah->cal_list_curr = currCal = currCal->calNext;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (currCal->calState == CAL_WAITING) {
148*4882a593Smuzhiyun iscaldone = false;
149*4882a593Smuzhiyun ath9k_hw_reset_calibration(ah, currCal);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun /*
155*4882a593Smuzhiyun * Do NF cal only at longer intervals. Get the value from
156*4882a593Smuzhiyun * the previous NF cal and update history buffer.
157*4882a593Smuzhiyun */
158*4882a593Smuzhiyun if (longcal && ath9k_hw_getnf(ah, chan)) {
159*4882a593Smuzhiyun /*
160*4882a593Smuzhiyun * Load the NF from history buffer of the current channel.
161*4882a593Smuzhiyun * NF is slow time-variant, so it is OK to use a historical
162*4882a593Smuzhiyun * value.
163*4882a593Smuzhiyun */
164*4882a593Smuzhiyun ret = ath9k_hw_loadnf(ah, ah->curchan);
165*4882a593Smuzhiyun if (ret < 0)
166*4882a593Smuzhiyun return ret;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun /* start NF calibration, without updating BB NF register */
169*4882a593Smuzhiyun ath9k_hw_start_nfcal(ah, false);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun return iscaldone;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
ar9003_hw_iqcal_collect(struct ath_hw * ah)175*4882a593Smuzhiyun static void ar9003_hw_iqcal_collect(struct ath_hw *ah)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun int i;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun /* Accumulate IQ cal measures for active chains */
180*4882a593Smuzhiyun for (i = 0; i < AR5416_MAX_CHAINS; i++) {
181*4882a593Smuzhiyun if (ah->txchainmask & BIT(i)) {
182*4882a593Smuzhiyun ah->totalPowerMeasI[i] +=
183*4882a593Smuzhiyun REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
184*4882a593Smuzhiyun ah->totalPowerMeasQ[i] +=
185*4882a593Smuzhiyun REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
186*4882a593Smuzhiyun ah->totalIqCorrMeas[i] +=
187*4882a593Smuzhiyun (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
188*4882a593Smuzhiyun ath_dbg(ath9k_hw_common(ah), CALIBRATE,
189*4882a593Smuzhiyun "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
190*4882a593Smuzhiyun ah->cal_samples, i, ah->totalPowerMeasI[i],
191*4882a593Smuzhiyun ah->totalPowerMeasQ[i],
192*4882a593Smuzhiyun ah->totalIqCorrMeas[i]);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
ar9003_hw_iqcalibrate(struct ath_hw * ah,u8 numChains)197*4882a593Smuzhiyun static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
200*4882a593Smuzhiyun u32 powerMeasQ, powerMeasI, iqCorrMeas;
201*4882a593Smuzhiyun u32 qCoffDenom, iCoffDenom;
202*4882a593Smuzhiyun int32_t qCoff, iCoff;
203*4882a593Smuzhiyun int iqCorrNeg, i;
204*4882a593Smuzhiyun static const u_int32_t offset_array[3] = {
205*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_B0,
206*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_B1,
207*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_B2,
208*4882a593Smuzhiyun };
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun for (i = 0; i < numChains; i++) {
211*4882a593Smuzhiyun powerMeasI = ah->totalPowerMeasI[i];
212*4882a593Smuzhiyun powerMeasQ = ah->totalPowerMeasQ[i];
213*4882a593Smuzhiyun iqCorrMeas = ah->totalIqCorrMeas[i];
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
216*4882a593Smuzhiyun "Starting IQ Cal and Correction for Chain %d\n", i);
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
219*4882a593Smuzhiyun "Original: Chn %d iq_corr_meas = 0x%08x\n",
220*4882a593Smuzhiyun i, ah->totalIqCorrMeas[i]);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun iqCorrNeg = 0;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun if (iqCorrMeas > 0x80000000) {
225*4882a593Smuzhiyun iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
226*4882a593Smuzhiyun iqCorrNeg = 1;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n",
230*4882a593Smuzhiyun i, powerMeasI);
231*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n",
232*4882a593Smuzhiyun i, powerMeasQ);
233*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg);
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256;
236*4882a593Smuzhiyun qCoffDenom = powerMeasQ / 64;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if ((iCoffDenom != 0) && (qCoffDenom != 0)) {
239*4882a593Smuzhiyun iCoff = iqCorrMeas / iCoffDenom;
240*4882a593Smuzhiyun qCoff = powerMeasI / qCoffDenom - 64;
241*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n",
242*4882a593Smuzhiyun i, iCoff);
243*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n",
244*4882a593Smuzhiyun i, qCoff);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun /* Force bounds on iCoff */
247*4882a593Smuzhiyun if (iCoff >= 63)
248*4882a593Smuzhiyun iCoff = 63;
249*4882a593Smuzhiyun else if (iCoff <= -63)
250*4882a593Smuzhiyun iCoff = -63;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun /* Negate iCoff if iqCorrNeg == 0 */
253*4882a593Smuzhiyun if (iqCorrNeg == 0x0)
254*4882a593Smuzhiyun iCoff = -iCoff;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun /* Force bounds on qCoff */
257*4882a593Smuzhiyun if (qCoff >= 63)
258*4882a593Smuzhiyun qCoff = 63;
259*4882a593Smuzhiyun else if (qCoff <= -63)
260*4882a593Smuzhiyun qCoff = -63;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun iCoff = iCoff & 0x7f;
263*4882a593Smuzhiyun qCoff = qCoff & 0x7f;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
266*4882a593Smuzhiyun "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
267*4882a593Smuzhiyun i, iCoff, qCoff);
268*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
269*4882a593Smuzhiyun "Register offset (0x%04x) before update = 0x%x\n",
270*4882a593Smuzhiyun offset_array[i],
271*4882a593Smuzhiyun REG_READ(ah, offset_array[i]));
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (AR_SREV_9565(ah) &&
274*4882a593Smuzhiyun (iCoff == 63 || qCoff == 63 ||
275*4882a593Smuzhiyun iCoff == -63 || qCoff == -63))
276*4882a593Smuzhiyun return;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun REG_RMW_FIELD(ah, offset_array[i],
279*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
280*4882a593Smuzhiyun iCoff);
281*4882a593Smuzhiyun REG_RMW_FIELD(ah, offset_array[i],
282*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
283*4882a593Smuzhiyun qCoff);
284*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
285*4882a593Smuzhiyun "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n",
286*4882a593Smuzhiyun offset_array[i],
287*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
288*4882a593Smuzhiyun REG_READ(ah, offset_array[i]));
289*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
290*4882a593Smuzhiyun "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n",
291*4882a593Smuzhiyun offset_array[i],
292*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
293*4882a593Smuzhiyun REG_READ(ah, offset_array[i]));
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
296*4882a593Smuzhiyun "IQ Cal and Correction done for Chain %d\n", i);
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0,
301*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE);
302*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
303*4882a593Smuzhiyun "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n",
304*4882a593Smuzhiyun (unsigned) (AR_PHY_RX_IQCAL_CORR_B0),
305*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE,
306*4882a593Smuzhiyun REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0));
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun static const struct ath9k_percal_data iq_cal_single_sample = {
310*4882a593Smuzhiyun IQ_MISMATCH_CAL,
311*4882a593Smuzhiyun MIN_CAL_SAMPLES,
312*4882a593Smuzhiyun PER_MAX_LOG_COUNT,
313*4882a593Smuzhiyun ar9003_hw_iqcal_collect,
314*4882a593Smuzhiyun ar9003_hw_iqcalibrate
315*4882a593Smuzhiyun };
316*4882a593Smuzhiyun
ar9003_hw_init_cal_settings(struct ath_hw * ah)317*4882a593Smuzhiyun static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun ah->iq_caldata.calData = &iq_cal_single_sample;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun if (AR_SREV_9300_20_OR_LATER(ah)) {
322*4882a593Smuzhiyun ah->enabled_cals |= TX_IQ_CAL;
323*4882a593Smuzhiyun if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah))
324*4882a593Smuzhiyun ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun ah->supp_cals = IQ_MISMATCH_CAL;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun #define OFF_UPPER_LT 24
331*4882a593Smuzhiyun #define OFF_LOWER_LT 7
332*4882a593Smuzhiyun
ar9003_hw_dynamic_osdac_selection(struct ath_hw * ah,bool txiqcal_done)333*4882a593Smuzhiyun static bool ar9003_hw_dynamic_osdac_selection(struct ath_hw *ah,
334*4882a593Smuzhiyun bool txiqcal_done)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
337*4882a593Smuzhiyun int ch0_done, osdac_ch0, dc_off_ch0_i1, dc_off_ch0_q1, dc_off_ch0_i2,
338*4882a593Smuzhiyun dc_off_ch0_q2, dc_off_ch0_i3, dc_off_ch0_q3;
339*4882a593Smuzhiyun int ch1_done, osdac_ch1, dc_off_ch1_i1, dc_off_ch1_q1, dc_off_ch1_i2,
340*4882a593Smuzhiyun dc_off_ch1_q2, dc_off_ch1_i3, dc_off_ch1_q3;
341*4882a593Smuzhiyun int ch2_done, osdac_ch2, dc_off_ch2_i1, dc_off_ch2_q1, dc_off_ch2_i2,
342*4882a593Smuzhiyun dc_off_ch2_q2, dc_off_ch2_i3, dc_off_ch2_q3;
343*4882a593Smuzhiyun bool status;
344*4882a593Smuzhiyun u32 temp, val;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /*
347*4882a593Smuzhiyun * Clear offset and IQ calibration, run AGC cal.
348*4882a593Smuzhiyun */
349*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
350*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_OFFSET_CAL);
351*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
352*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
353*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL,
354*4882a593Smuzhiyun REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL);
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
357*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL,
358*4882a593Smuzhiyun 0, AH_WAIT_TIMEOUT);
359*4882a593Smuzhiyun if (!status) {
360*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
361*4882a593Smuzhiyun "AGC cal without offset cal failed to complete in 1ms");
362*4882a593Smuzhiyun return false;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /*
366*4882a593Smuzhiyun * Allow only offset calibration and disable the others
367*4882a593Smuzhiyun * (Carrier Leak calibration, TX Filter calibration and
368*4882a593Smuzhiyun * Peak Detector offset calibration).
369*4882a593Smuzhiyun */
370*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
371*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_OFFSET_CAL);
372*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
373*4882a593Smuzhiyun AR_PHY_CL_CAL_ENABLE);
374*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
375*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_FLTR_CAL);
376*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
377*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_PKDET_CAL);
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun ch0_done = 0;
380*4882a593Smuzhiyun ch1_done = 0;
381*4882a593Smuzhiyun ch2_done = 0;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun while ((ch0_done == 0) || (ch1_done == 0) || (ch2_done == 0)) {
384*4882a593Smuzhiyun osdac_ch0 = (REG_READ(ah, AR_PHY_65NM_CH0_BB1) >> 30) & 0x3;
385*4882a593Smuzhiyun osdac_ch1 = (REG_READ(ah, AR_PHY_65NM_CH1_BB1) >> 30) & 0x3;
386*4882a593Smuzhiyun osdac_ch2 = (REG_READ(ah, AR_PHY_65NM_CH2_BB1) >> 30) & 0x3;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL,
391*4882a593Smuzhiyun REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
394*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL,
395*4882a593Smuzhiyun 0, AH_WAIT_TIMEOUT);
396*4882a593Smuzhiyun if (!status) {
397*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
398*4882a593Smuzhiyun "DC offset cal failed to complete in 1ms");
399*4882a593Smuzhiyun return false;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun /*
405*4882a593Smuzhiyun * High gain.
406*4882a593Smuzhiyun */
407*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
408*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (1 << 8)));
409*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
410*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (1 << 8)));
411*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
412*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (1 << 8)));
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
415*4882a593Smuzhiyun dc_off_ch0_i1 = (temp >> 26) & 0x1f;
416*4882a593Smuzhiyun dc_off_ch0_q1 = (temp >> 21) & 0x1f;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
419*4882a593Smuzhiyun dc_off_ch1_i1 = (temp >> 26) & 0x1f;
420*4882a593Smuzhiyun dc_off_ch1_q1 = (temp >> 21) & 0x1f;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
423*4882a593Smuzhiyun dc_off_ch2_i1 = (temp >> 26) & 0x1f;
424*4882a593Smuzhiyun dc_off_ch2_q1 = (temp >> 21) & 0x1f;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /*
427*4882a593Smuzhiyun * Low gain.
428*4882a593Smuzhiyun */
429*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
430*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (2 << 8)));
431*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
432*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (2 << 8)));
433*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
434*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (2 << 8)));
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
437*4882a593Smuzhiyun dc_off_ch0_i2 = (temp >> 26) & 0x1f;
438*4882a593Smuzhiyun dc_off_ch0_q2 = (temp >> 21) & 0x1f;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
441*4882a593Smuzhiyun dc_off_ch1_i2 = (temp >> 26) & 0x1f;
442*4882a593Smuzhiyun dc_off_ch1_q2 = (temp >> 21) & 0x1f;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
445*4882a593Smuzhiyun dc_off_ch2_i2 = (temp >> 26) & 0x1f;
446*4882a593Smuzhiyun dc_off_ch2_q2 = (temp >> 21) & 0x1f;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun /*
449*4882a593Smuzhiyun * Loopback.
450*4882a593Smuzhiyun */
451*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
452*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (3 << 8)));
453*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
454*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (3 << 8)));
455*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
456*4882a593Smuzhiyun ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (3 << 8)));
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
459*4882a593Smuzhiyun dc_off_ch0_i3 = (temp >> 26) & 0x1f;
460*4882a593Smuzhiyun dc_off_ch0_q3 = (temp >> 21) & 0x1f;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
463*4882a593Smuzhiyun dc_off_ch1_i3 = (temp >> 26) & 0x1f;
464*4882a593Smuzhiyun dc_off_ch1_q3 = (temp >> 21) & 0x1f;
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
467*4882a593Smuzhiyun dc_off_ch2_i3 = (temp >> 26) & 0x1f;
468*4882a593Smuzhiyun dc_off_ch2_q3 = (temp >> 21) & 0x1f;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun if ((dc_off_ch0_i1 > OFF_UPPER_LT) || (dc_off_ch0_i1 < OFF_LOWER_LT) ||
471*4882a593Smuzhiyun (dc_off_ch0_i2 > OFF_UPPER_LT) || (dc_off_ch0_i2 < OFF_LOWER_LT) ||
472*4882a593Smuzhiyun (dc_off_ch0_i3 > OFF_UPPER_LT) || (dc_off_ch0_i3 < OFF_LOWER_LT) ||
473*4882a593Smuzhiyun (dc_off_ch0_q1 > OFF_UPPER_LT) || (dc_off_ch0_q1 < OFF_LOWER_LT) ||
474*4882a593Smuzhiyun (dc_off_ch0_q2 > OFF_UPPER_LT) || (dc_off_ch0_q2 < OFF_LOWER_LT) ||
475*4882a593Smuzhiyun (dc_off_ch0_q3 > OFF_UPPER_LT) || (dc_off_ch0_q3 < OFF_LOWER_LT)) {
476*4882a593Smuzhiyun if (osdac_ch0 == 3) {
477*4882a593Smuzhiyun ch0_done = 1;
478*4882a593Smuzhiyun } else {
479*4882a593Smuzhiyun osdac_ch0++;
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun val = REG_READ(ah, AR_PHY_65NM_CH0_BB1) & 0x3fffffff;
482*4882a593Smuzhiyun val |= (osdac_ch0 << 30);
483*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, val);
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun ch0_done = 0;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun } else {
488*4882a593Smuzhiyun ch0_done = 1;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if ((dc_off_ch1_i1 > OFF_UPPER_LT) || (dc_off_ch1_i1 < OFF_LOWER_LT) ||
492*4882a593Smuzhiyun (dc_off_ch1_i2 > OFF_UPPER_LT) || (dc_off_ch1_i2 < OFF_LOWER_LT) ||
493*4882a593Smuzhiyun (dc_off_ch1_i3 > OFF_UPPER_LT) || (dc_off_ch1_i3 < OFF_LOWER_LT) ||
494*4882a593Smuzhiyun (dc_off_ch1_q1 > OFF_UPPER_LT) || (dc_off_ch1_q1 < OFF_LOWER_LT) ||
495*4882a593Smuzhiyun (dc_off_ch1_q2 > OFF_UPPER_LT) || (dc_off_ch1_q2 < OFF_LOWER_LT) ||
496*4882a593Smuzhiyun (dc_off_ch1_q3 > OFF_UPPER_LT) || (dc_off_ch1_q3 < OFF_LOWER_LT)) {
497*4882a593Smuzhiyun if (osdac_ch1 == 3) {
498*4882a593Smuzhiyun ch1_done = 1;
499*4882a593Smuzhiyun } else {
500*4882a593Smuzhiyun osdac_ch1++;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun val = REG_READ(ah, AR_PHY_65NM_CH1_BB1) & 0x3fffffff;
503*4882a593Smuzhiyun val |= (osdac_ch1 << 30);
504*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, val);
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun ch1_done = 0;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun } else {
509*4882a593Smuzhiyun ch1_done = 1;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun if ((dc_off_ch2_i1 > OFF_UPPER_LT) || (dc_off_ch2_i1 < OFF_LOWER_LT) ||
513*4882a593Smuzhiyun (dc_off_ch2_i2 > OFF_UPPER_LT) || (dc_off_ch2_i2 < OFF_LOWER_LT) ||
514*4882a593Smuzhiyun (dc_off_ch2_i3 > OFF_UPPER_LT) || (dc_off_ch2_i3 < OFF_LOWER_LT) ||
515*4882a593Smuzhiyun (dc_off_ch2_q1 > OFF_UPPER_LT) || (dc_off_ch2_q1 < OFF_LOWER_LT) ||
516*4882a593Smuzhiyun (dc_off_ch2_q2 > OFF_UPPER_LT) || (dc_off_ch2_q2 < OFF_LOWER_LT) ||
517*4882a593Smuzhiyun (dc_off_ch2_q3 > OFF_UPPER_LT) || (dc_off_ch2_q3 < OFF_LOWER_LT)) {
518*4882a593Smuzhiyun if (osdac_ch2 == 3) {
519*4882a593Smuzhiyun ch2_done = 1;
520*4882a593Smuzhiyun } else {
521*4882a593Smuzhiyun osdac_ch2++;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun val = REG_READ(ah, AR_PHY_65NM_CH2_BB1) & 0x3fffffff;
524*4882a593Smuzhiyun val |= (osdac_ch2 << 30);
525*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, val);
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun ch2_done = 0;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun } else {
530*4882a593Smuzhiyun ch2_done = 1;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
535*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_OFFSET_CAL);
536*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun /*
539*4882a593Smuzhiyun * We don't need to check txiqcal_done here since it is always
540*4882a593Smuzhiyun * set for AR9550.
541*4882a593Smuzhiyun */
542*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
543*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun return true;
546*4882a593Smuzhiyun }
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun /*
549*4882a593Smuzhiyun * solve 4x4 linear equation used in loopback iq cal.
550*4882a593Smuzhiyun */
ar9003_hw_solve_iq_cal(struct ath_hw * ah,s32 sin_2phi_1,s32 cos_2phi_1,s32 sin_2phi_2,s32 cos_2phi_2,s32 mag_a0_d0,s32 phs_a0_d0,s32 mag_a1_d0,s32 phs_a1_d0,s32 solved_eq[])551*4882a593Smuzhiyun static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah,
552*4882a593Smuzhiyun s32 sin_2phi_1,
553*4882a593Smuzhiyun s32 cos_2phi_1,
554*4882a593Smuzhiyun s32 sin_2phi_2,
555*4882a593Smuzhiyun s32 cos_2phi_2,
556*4882a593Smuzhiyun s32 mag_a0_d0,
557*4882a593Smuzhiyun s32 phs_a0_d0,
558*4882a593Smuzhiyun s32 mag_a1_d0,
559*4882a593Smuzhiyun s32 phs_a1_d0,
560*4882a593Smuzhiyun s32 solved_eq[])
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun s32 f1 = cos_2phi_1 - cos_2phi_2,
563*4882a593Smuzhiyun f3 = sin_2phi_1 - sin_2phi_2,
564*4882a593Smuzhiyun f2;
565*4882a593Smuzhiyun s32 mag_tx, phs_tx, mag_rx, phs_rx;
566*4882a593Smuzhiyun const s32 result_shift = 1 << 15;
567*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun f2 = ((f1 >> 3) * (f1 >> 3) + (f3 >> 3) * (f3 >> 3)) >> 9;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun if (!f2) {
572*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Divide by 0\n");
573*4882a593Smuzhiyun return false;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun /* mag mismatch, tx */
577*4882a593Smuzhiyun mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0);
578*4882a593Smuzhiyun /* phs mismatch, tx */
579*4882a593Smuzhiyun phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0);
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun mag_tx = (mag_tx / f2);
582*4882a593Smuzhiyun phs_tx = (phs_tx / f2);
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun /* mag mismatch, rx */
585*4882a593Smuzhiyun mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) /
586*4882a593Smuzhiyun result_shift;
587*4882a593Smuzhiyun /* phs mismatch, rx */
588*4882a593Smuzhiyun phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) /
589*4882a593Smuzhiyun result_shift;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun solved_eq[0] = mag_tx;
592*4882a593Smuzhiyun solved_eq[1] = phs_tx;
593*4882a593Smuzhiyun solved_eq[2] = mag_rx;
594*4882a593Smuzhiyun solved_eq[3] = phs_rx;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun return true;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun
ar9003_hw_find_mag_approx(struct ath_hw * ah,s32 in_re,s32 in_im)599*4882a593Smuzhiyun static s32 ar9003_hw_find_mag_approx(struct ath_hw *ah, s32 in_re, s32 in_im)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun s32 abs_i = abs(in_re),
602*4882a593Smuzhiyun abs_q = abs(in_im),
603*4882a593Smuzhiyun max_abs, min_abs;
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun if (abs_i > abs_q) {
606*4882a593Smuzhiyun max_abs = abs_i;
607*4882a593Smuzhiyun min_abs = abs_q;
608*4882a593Smuzhiyun } else {
609*4882a593Smuzhiyun max_abs = abs_q;
610*4882a593Smuzhiyun min_abs = abs_i;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun return max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4);
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun #define DELPT 32
617*4882a593Smuzhiyun
ar9003_hw_calc_iq_corr(struct ath_hw * ah,s32 chain_idx,const s32 iq_res[],s32 iqc_coeff[])618*4882a593Smuzhiyun static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
619*4882a593Smuzhiyun s32 chain_idx,
620*4882a593Smuzhiyun const s32 iq_res[],
621*4882a593Smuzhiyun s32 iqc_coeff[])
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun s32 i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0,
624*4882a593Smuzhiyun i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1,
625*4882a593Smuzhiyun i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0,
626*4882a593Smuzhiyun i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1;
627*4882a593Smuzhiyun s32 mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1,
628*4882a593Smuzhiyun phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1,
629*4882a593Smuzhiyun sin_2phi_1, cos_2phi_1,
630*4882a593Smuzhiyun sin_2phi_2, cos_2phi_2;
631*4882a593Smuzhiyun s32 mag_tx, phs_tx, mag_rx, phs_rx;
632*4882a593Smuzhiyun s32 solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx,
633*4882a593Smuzhiyun q_q_coff, q_i_coff;
634*4882a593Smuzhiyun const s32 res_scale = 1 << 15;
635*4882a593Smuzhiyun const s32 delpt_shift = 1 << 8;
636*4882a593Smuzhiyun s32 mag1, mag2;
637*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
638*4882a593Smuzhiyun
639*4882a593Smuzhiyun i2_m_q2_a0_d0 = iq_res[0] & 0xfff;
640*4882a593Smuzhiyun i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff;
641*4882a593Smuzhiyun iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8);
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun if (i2_m_q2_a0_d0 > 0x800)
644*4882a593Smuzhiyun i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1);
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun if (i2_p_q2_a0_d0 > 0x800)
647*4882a593Smuzhiyun i2_p_q2_a0_d0 = -((0xfff - i2_p_q2_a0_d0) + 1);
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun if (iq_corr_a0_d0 > 0x800)
650*4882a593Smuzhiyun iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1);
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff;
653*4882a593Smuzhiyun i2_p_q2_a0_d1 = (iq_res[2] & 0xfff);
654*4882a593Smuzhiyun iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff;
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun if (i2_m_q2_a0_d1 > 0x800)
657*4882a593Smuzhiyun i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1);
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun if (iq_corr_a0_d1 > 0x800)
660*4882a593Smuzhiyun iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8);
663*4882a593Smuzhiyun i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff;
664*4882a593Smuzhiyun iq_corr_a1_d0 = iq_res[4] & 0xfff;
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun if (i2_m_q2_a1_d0 > 0x800)
667*4882a593Smuzhiyun i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1);
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun if (i2_p_q2_a1_d0 > 0x800)
670*4882a593Smuzhiyun i2_p_q2_a1_d0 = -((0xfff - i2_p_q2_a1_d0) + 1);
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun if (iq_corr_a1_d0 > 0x800)
673*4882a593Smuzhiyun iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1);
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff;
676*4882a593Smuzhiyun i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8);
677*4882a593Smuzhiyun iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff;
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun if (i2_m_q2_a1_d1 > 0x800)
680*4882a593Smuzhiyun i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1);
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun if (i2_p_q2_a1_d1 > 0x800)
683*4882a593Smuzhiyun i2_p_q2_a1_d1 = -((0xfff - i2_p_q2_a1_d1) + 1);
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun if (iq_corr_a1_d1 > 0x800)
686*4882a593Smuzhiyun iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1);
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) ||
689*4882a593Smuzhiyun (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) {
690*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
691*4882a593Smuzhiyun "Divide by 0:\n"
692*4882a593Smuzhiyun "a0_d0=%d\n"
693*4882a593Smuzhiyun "a0_d1=%d\n"
694*4882a593Smuzhiyun "a2_d0=%d\n"
695*4882a593Smuzhiyun "a1_d1=%d\n",
696*4882a593Smuzhiyun i2_p_q2_a0_d0, i2_p_q2_a0_d1,
697*4882a593Smuzhiyun i2_p_q2_a1_d0, i2_p_q2_a1_d1);
698*4882a593Smuzhiyun return false;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun if ((i2_p_q2_a0_d0 < 1024) || (i2_p_q2_a0_d0 > 2047) ||
702*4882a593Smuzhiyun (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) ||
703*4882a593Smuzhiyun (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) ||
704*4882a593Smuzhiyun (i2_p_q2_a0_d0 <= iq_corr_a0_d0) ||
705*4882a593Smuzhiyun (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) ||
706*4882a593Smuzhiyun (i2_p_q2_a0_d1 <= iq_corr_a0_d1) ||
707*4882a593Smuzhiyun (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) ||
708*4882a593Smuzhiyun (i2_p_q2_a1_d0 <= iq_corr_a1_d0) ||
709*4882a593Smuzhiyun (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) ||
710*4882a593Smuzhiyun (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) {
711*4882a593Smuzhiyun return false;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun
714*4882a593Smuzhiyun mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0;
715*4882a593Smuzhiyun phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0;
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1;
718*4882a593Smuzhiyun phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1;
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0;
721*4882a593Smuzhiyun phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1;
724*4882a593Smuzhiyun phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1;
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun /* w/o analog phase shift */
727*4882a593Smuzhiyun sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT);
728*4882a593Smuzhiyun /* w/o analog phase shift */
729*4882a593Smuzhiyun cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT);
730*4882a593Smuzhiyun /* w/ analog phase shift */
731*4882a593Smuzhiyun sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT);
732*4882a593Smuzhiyun /* w/ analog phase shift */
733*4882a593Smuzhiyun cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT);
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun /*
736*4882a593Smuzhiyun * force sin^2 + cos^2 = 1;
737*4882a593Smuzhiyun * find magnitude by approximation
738*4882a593Smuzhiyun */
739*4882a593Smuzhiyun mag1 = ar9003_hw_find_mag_approx(ah, cos_2phi_1, sin_2phi_1);
740*4882a593Smuzhiyun mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2);
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun if ((mag1 == 0) || (mag2 == 0)) {
743*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Divide by 0: mag1=%d, mag2=%d\n",
744*4882a593Smuzhiyun mag1, mag2);
745*4882a593Smuzhiyun return false;
746*4882a593Smuzhiyun }
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun /* normalization sin and cos by mag */
749*4882a593Smuzhiyun sin_2phi_1 = (sin_2phi_1 * res_scale / mag1);
750*4882a593Smuzhiyun cos_2phi_1 = (cos_2phi_1 * res_scale / mag1);
751*4882a593Smuzhiyun sin_2phi_2 = (sin_2phi_2 * res_scale / mag2);
752*4882a593Smuzhiyun cos_2phi_2 = (cos_2phi_2 * res_scale / mag2);
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun /* calculate IQ mismatch */
755*4882a593Smuzhiyun if (!ar9003_hw_solve_iq_cal(ah,
756*4882a593Smuzhiyun sin_2phi_1, cos_2phi_1,
757*4882a593Smuzhiyun sin_2phi_2, cos_2phi_2,
758*4882a593Smuzhiyun mag_a0_d0, phs_a0_d0,
759*4882a593Smuzhiyun mag_a1_d0,
760*4882a593Smuzhiyun phs_a1_d0, solved_eq)) {
761*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
762*4882a593Smuzhiyun "Call to ar9003_hw_solve_iq_cal() failed\n");
763*4882a593Smuzhiyun return false;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun mag_tx = solved_eq[0];
767*4882a593Smuzhiyun phs_tx = solved_eq[1];
768*4882a593Smuzhiyun mag_rx = solved_eq[2];
769*4882a593Smuzhiyun phs_rx = solved_eq[3];
770*4882a593Smuzhiyun
771*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
772*4882a593Smuzhiyun "chain %d: mag mismatch=%d phase mismatch=%d\n",
773*4882a593Smuzhiyun chain_idx, mag_tx/res_scale, phs_tx/res_scale);
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun if (res_scale == mag_tx) {
776*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
777*4882a593Smuzhiyun "Divide by 0: mag_tx=%d, res_scale=%d\n",
778*4882a593Smuzhiyun mag_tx, res_scale);
779*4882a593Smuzhiyun return false;
780*4882a593Smuzhiyun }
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun /* calculate and quantize Tx IQ correction factor */
783*4882a593Smuzhiyun mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx);
784*4882a593Smuzhiyun phs_corr_tx = -phs_tx;
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun q_q_coff = (mag_corr_tx * 128 / res_scale);
787*4882a593Smuzhiyun q_i_coff = (phs_corr_tx * 256 / res_scale);
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "tx chain %d: mag corr=%d phase corr=%d\n",
790*4882a593Smuzhiyun chain_idx, q_q_coff, q_i_coff);
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun if (q_i_coff < -63)
793*4882a593Smuzhiyun q_i_coff = -63;
794*4882a593Smuzhiyun if (q_i_coff > 63)
795*4882a593Smuzhiyun q_i_coff = 63;
796*4882a593Smuzhiyun if (q_q_coff < -63)
797*4882a593Smuzhiyun q_q_coff = -63;
798*4882a593Smuzhiyun if (q_q_coff > 63)
799*4882a593Smuzhiyun q_q_coff = 63;
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff);
802*4882a593Smuzhiyun
803*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n",
804*4882a593Smuzhiyun chain_idx, iqc_coeff[0]);
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun if (-mag_rx == res_scale) {
807*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
808*4882a593Smuzhiyun "Divide by 0: mag_rx=%d, res_scale=%d\n",
809*4882a593Smuzhiyun mag_rx, res_scale);
810*4882a593Smuzhiyun return false;
811*4882a593Smuzhiyun }
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun /* calculate and quantize Rx IQ correction factors */
814*4882a593Smuzhiyun mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx);
815*4882a593Smuzhiyun phs_corr_rx = -phs_rx;
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun q_q_coff = (mag_corr_rx * 128 / res_scale);
818*4882a593Smuzhiyun q_i_coff = (phs_corr_rx * 256 / res_scale);
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "rx chain %d: mag corr=%d phase corr=%d\n",
821*4882a593Smuzhiyun chain_idx, q_q_coff, q_i_coff);
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun if (q_i_coff < -63)
824*4882a593Smuzhiyun q_i_coff = -63;
825*4882a593Smuzhiyun if (q_i_coff > 63)
826*4882a593Smuzhiyun q_i_coff = 63;
827*4882a593Smuzhiyun if (q_q_coff < -63)
828*4882a593Smuzhiyun q_q_coff = -63;
829*4882a593Smuzhiyun if (q_q_coff > 63)
830*4882a593Smuzhiyun q_q_coff = 63;
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff);
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
835*4882a593Smuzhiyun chain_idx, iqc_coeff[1]);
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun return true;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun
ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL],int nmeasurement,int max_delta)840*4882a593Smuzhiyun static void ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL],
841*4882a593Smuzhiyun int nmeasurement,
842*4882a593Smuzhiyun int max_delta)
843*4882a593Smuzhiyun {
844*4882a593Smuzhiyun int mp_max = -64, max_idx = 0;
845*4882a593Smuzhiyun int mp_min = 63, min_idx = 0;
846*4882a593Smuzhiyun int mp_avg = 0, i, outlier_idx = 0, mp_count = 0;
847*4882a593Smuzhiyun
848*4882a593Smuzhiyun /* find min/max mismatch across all calibrated gains */
849*4882a593Smuzhiyun for (i = 0; i < nmeasurement; i++) {
850*4882a593Smuzhiyun if (mp_coeff[i][0] > mp_max) {
851*4882a593Smuzhiyun mp_max = mp_coeff[i][0];
852*4882a593Smuzhiyun max_idx = i;
853*4882a593Smuzhiyun } else if (mp_coeff[i][0] < mp_min) {
854*4882a593Smuzhiyun mp_min = mp_coeff[i][0];
855*4882a593Smuzhiyun min_idx = i;
856*4882a593Smuzhiyun }
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun /* find average (exclude max abs value) */
860*4882a593Smuzhiyun for (i = 0; i < nmeasurement; i++) {
861*4882a593Smuzhiyun if ((abs(mp_coeff[i][0]) < abs(mp_max)) ||
862*4882a593Smuzhiyun (abs(mp_coeff[i][0]) < abs(mp_min))) {
863*4882a593Smuzhiyun mp_avg += mp_coeff[i][0];
864*4882a593Smuzhiyun mp_count++;
865*4882a593Smuzhiyun }
866*4882a593Smuzhiyun }
867*4882a593Smuzhiyun
868*4882a593Smuzhiyun /*
869*4882a593Smuzhiyun * finding mean magnitude/phase if possible, otherwise
870*4882a593Smuzhiyun * just use the last value as the mean
871*4882a593Smuzhiyun */
872*4882a593Smuzhiyun if (mp_count)
873*4882a593Smuzhiyun mp_avg /= mp_count;
874*4882a593Smuzhiyun else
875*4882a593Smuzhiyun mp_avg = mp_coeff[nmeasurement - 1][0];
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun /* detect outlier */
878*4882a593Smuzhiyun if (abs(mp_max - mp_min) > max_delta) {
879*4882a593Smuzhiyun if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg))
880*4882a593Smuzhiyun outlier_idx = max_idx;
881*4882a593Smuzhiyun else
882*4882a593Smuzhiyun outlier_idx = min_idx;
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun mp_coeff[outlier_idx][0] = mp_avg;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun }
887*4882a593Smuzhiyun
ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw * ah,struct coeff * coeff,bool is_reusable)888*4882a593Smuzhiyun static void ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw *ah,
889*4882a593Smuzhiyun struct coeff *coeff,
890*4882a593Smuzhiyun bool is_reusable)
891*4882a593Smuzhiyun {
892*4882a593Smuzhiyun int i, im, nmeasurement;
893*4882a593Smuzhiyun int magnitude, phase;
894*4882a593Smuzhiyun u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
895*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
898*4882a593Smuzhiyun for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
899*4882a593Smuzhiyun tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] =
900*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B0(i);
901*4882a593Smuzhiyun if (!AR_SREV_9485(ah)) {
902*4882a593Smuzhiyun tx_corr_coeff[i * 2][1] =
903*4882a593Smuzhiyun tx_corr_coeff[(i * 2) + 1][1] =
904*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B1(i);
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun tx_corr_coeff[i * 2][2] =
907*4882a593Smuzhiyun tx_corr_coeff[(i * 2) + 1][2] =
908*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B2(i);
909*4882a593Smuzhiyun }
910*4882a593Smuzhiyun }
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun /* Load the average of 2 passes */
913*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
914*4882a593Smuzhiyun if (!(ah->txchainmask & (1 << i)))
915*4882a593Smuzhiyun continue;
916*4882a593Smuzhiyun nmeasurement = REG_READ_FIELD(ah,
917*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_B0,
918*4882a593Smuzhiyun AR_PHY_CALIBRATED_GAINS_0);
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun if (nmeasurement > MAX_MEASUREMENT)
921*4882a593Smuzhiyun nmeasurement = MAX_MEASUREMENT;
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun /*
924*4882a593Smuzhiyun * Skip normal outlier detection for AR9550.
925*4882a593Smuzhiyun */
926*4882a593Smuzhiyun if (!AR_SREV_9550(ah)) {
927*4882a593Smuzhiyun /* detect outlier only if nmeasurement > 1 */
928*4882a593Smuzhiyun if (nmeasurement > 1) {
929*4882a593Smuzhiyun /* Detect magnitude outlier */
930*4882a593Smuzhiyun ar9003_hw_detect_outlier(coeff->mag_coeff[i],
931*4882a593Smuzhiyun nmeasurement,
932*4882a593Smuzhiyun MAX_MAG_DELTA);
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun /* Detect phase outlier */
935*4882a593Smuzhiyun ar9003_hw_detect_outlier(coeff->phs_coeff[i],
936*4882a593Smuzhiyun nmeasurement,
937*4882a593Smuzhiyun MAX_PHS_DELTA);
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun for (im = 0; im < nmeasurement; im++) {
942*4882a593Smuzhiyun magnitude = coeff->mag_coeff[i][im][0];
943*4882a593Smuzhiyun phase = coeff->phs_coeff[i][im][0];
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun coeff->iqc_coeff[0] =
946*4882a593Smuzhiyun (phase & 0x7f) | ((magnitude & 0x7f) << 7);
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun if ((im % 2) == 0)
949*4882a593Smuzhiyun REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
950*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
951*4882a593Smuzhiyun coeff->iqc_coeff[0]);
952*4882a593Smuzhiyun else
953*4882a593Smuzhiyun REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
954*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
955*4882a593Smuzhiyun coeff->iqc_coeff[0]);
956*4882a593Smuzhiyun
957*4882a593Smuzhiyun if (caldata)
958*4882a593Smuzhiyun caldata->tx_corr_coeff[im][i] =
959*4882a593Smuzhiyun coeff->iqc_coeff[0];
960*4882a593Smuzhiyun }
961*4882a593Smuzhiyun if (caldata)
962*4882a593Smuzhiyun caldata->num_measures[i] = nmeasurement;
963*4882a593Smuzhiyun }
964*4882a593Smuzhiyun
965*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
966*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
967*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
968*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun if (caldata) {
971*4882a593Smuzhiyun if (is_reusable)
972*4882a593Smuzhiyun set_bit(TXIQCAL_DONE, &caldata->cal_flags);
973*4882a593Smuzhiyun else
974*4882a593Smuzhiyun clear_bit(TXIQCAL_DONE, &caldata->cal_flags);
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun return;
978*4882a593Smuzhiyun }
979*4882a593Smuzhiyun
ar9003_hw_tx_iq_cal_run(struct ath_hw * ah)980*4882a593Smuzhiyun static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
981*4882a593Smuzhiyun {
982*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
983*4882a593Smuzhiyun u8 tx_gain_forced;
984*4882a593Smuzhiyun
985*4882a593Smuzhiyun tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
986*4882a593Smuzhiyun AR_PHY_TXGAIN_FORCE);
987*4882a593Smuzhiyun if (tx_gain_forced)
988*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
989*4882a593Smuzhiyun AR_PHY_TXGAIN_FORCE, 0);
990*4882a593Smuzhiyun
991*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
992*4882a593Smuzhiyun AR_PHY_TX_IQCAL_START_DO_CAL, 1);
993*4882a593Smuzhiyun
994*4882a593Smuzhiyun if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
995*4882a593Smuzhiyun AR_PHY_TX_IQCAL_START_DO_CAL, 0,
996*4882a593Smuzhiyun AH_WAIT_TIMEOUT)) {
997*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Tx IQ Cal is not completed\n");
998*4882a593Smuzhiyun return false;
999*4882a593Smuzhiyun }
1000*4882a593Smuzhiyun return true;
1001*4882a593Smuzhiyun }
1002*4882a593Smuzhiyun
__ar955x_tx_iq_cal_sort(struct ath_hw * ah,struct coeff * coeff,int i,int nmeasurement)1003*4882a593Smuzhiyun static void __ar955x_tx_iq_cal_sort(struct ath_hw *ah,
1004*4882a593Smuzhiyun struct coeff *coeff,
1005*4882a593Smuzhiyun int i, int nmeasurement)
1006*4882a593Smuzhiyun {
1007*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
1008*4882a593Smuzhiyun int im, ix, iy, temp;
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun for (im = 0; im < nmeasurement; im++) {
1011*4882a593Smuzhiyun for (ix = 0; ix < MAXIQCAL - 1; ix++) {
1012*4882a593Smuzhiyun for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) {
1013*4882a593Smuzhiyun if (coeff->mag_coeff[i][im][iy] <
1014*4882a593Smuzhiyun coeff->mag_coeff[i][im][ix]) {
1015*4882a593Smuzhiyun temp = coeff->mag_coeff[i][im][ix];
1016*4882a593Smuzhiyun coeff->mag_coeff[i][im][ix] =
1017*4882a593Smuzhiyun coeff->mag_coeff[i][im][iy];
1018*4882a593Smuzhiyun coeff->mag_coeff[i][im][iy] = temp;
1019*4882a593Smuzhiyun }
1020*4882a593Smuzhiyun if (coeff->phs_coeff[i][im][iy] <
1021*4882a593Smuzhiyun coeff->phs_coeff[i][im][ix]) {
1022*4882a593Smuzhiyun temp = coeff->phs_coeff[i][im][ix];
1023*4882a593Smuzhiyun coeff->phs_coeff[i][im][ix] =
1024*4882a593Smuzhiyun coeff->phs_coeff[i][im][iy];
1025*4882a593Smuzhiyun coeff->phs_coeff[i][im][iy] = temp;
1026*4882a593Smuzhiyun }
1027*4882a593Smuzhiyun }
1028*4882a593Smuzhiyun }
1029*4882a593Smuzhiyun coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2];
1030*4882a593Smuzhiyun coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2];
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1033*4882a593Smuzhiyun "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n",
1034*4882a593Smuzhiyun i, im,
1035*4882a593Smuzhiyun coeff->mag_coeff[i][im][0],
1036*4882a593Smuzhiyun coeff->phs_coeff[i][im][0]);
1037*4882a593Smuzhiyun }
1038*4882a593Smuzhiyun }
1039*4882a593Smuzhiyun
ar955x_tx_iq_cal_median(struct ath_hw * ah,struct coeff * coeff,int iqcal_idx,int nmeasurement)1040*4882a593Smuzhiyun static bool ar955x_tx_iq_cal_median(struct ath_hw *ah,
1041*4882a593Smuzhiyun struct coeff *coeff,
1042*4882a593Smuzhiyun int iqcal_idx,
1043*4882a593Smuzhiyun int nmeasurement)
1044*4882a593Smuzhiyun {
1045*4882a593Smuzhiyun int i;
1046*4882a593Smuzhiyun
1047*4882a593Smuzhiyun if ((iqcal_idx + 1) != MAXIQCAL)
1048*4882a593Smuzhiyun return false;
1049*4882a593Smuzhiyun
1050*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1051*4882a593Smuzhiyun __ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement);
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun
1054*4882a593Smuzhiyun return true;
1055*4882a593Smuzhiyun }
1056*4882a593Smuzhiyun
ar9003_hw_tx_iq_cal_post_proc(struct ath_hw * ah,int iqcal_idx,bool is_reusable)1057*4882a593Smuzhiyun static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah,
1058*4882a593Smuzhiyun int iqcal_idx,
1059*4882a593Smuzhiyun bool is_reusable)
1060*4882a593Smuzhiyun {
1061*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
1062*4882a593Smuzhiyun const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
1063*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_B0,
1064*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_B1,
1065*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_B2,
1066*4882a593Smuzhiyun };
1067*4882a593Smuzhiyun const u_int32_t chan_info_tab[] = {
1068*4882a593Smuzhiyun AR_PHY_CHAN_INFO_TAB_0,
1069*4882a593Smuzhiyun AR_PHY_CHAN_INFO_TAB_1,
1070*4882a593Smuzhiyun AR_PHY_CHAN_INFO_TAB_2,
1071*4882a593Smuzhiyun };
1072*4882a593Smuzhiyun static struct coeff coeff;
1073*4882a593Smuzhiyun s32 iq_res[6];
1074*4882a593Smuzhiyun int i, im, j;
1075*4882a593Smuzhiyun int nmeasurement = 0;
1076*4882a593Smuzhiyun bool outlier_detect = true;
1077*4882a593Smuzhiyun
1078*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1079*4882a593Smuzhiyun if (!(ah->txchainmask & (1 << i)))
1080*4882a593Smuzhiyun continue;
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun nmeasurement = REG_READ_FIELD(ah,
1083*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_B0,
1084*4882a593Smuzhiyun AR_PHY_CALIBRATED_GAINS_0);
1085*4882a593Smuzhiyun if (nmeasurement > MAX_MEASUREMENT)
1086*4882a593Smuzhiyun nmeasurement = MAX_MEASUREMENT;
1087*4882a593Smuzhiyun
1088*4882a593Smuzhiyun for (im = 0; im < nmeasurement; im++) {
1089*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1090*4882a593Smuzhiyun "Doing Tx IQ Cal for chain %d\n", i);
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun if (REG_READ(ah, txiqcal_status[i]) &
1093*4882a593Smuzhiyun AR_PHY_TX_IQCAL_STATUS_FAILED) {
1094*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1095*4882a593Smuzhiyun "Tx IQ Cal failed for chain %d\n", i);
1096*4882a593Smuzhiyun goto tx_iqcal_fail;
1097*4882a593Smuzhiyun }
1098*4882a593Smuzhiyun
1099*4882a593Smuzhiyun for (j = 0; j < 3; j++) {
1100*4882a593Smuzhiyun u32 idx = 2 * j, offset = 4 * (3 * im + j);
1101*4882a593Smuzhiyun
1102*4882a593Smuzhiyun REG_RMW_FIELD(ah,
1103*4882a593Smuzhiyun AR_PHY_CHAN_INFO_MEMORY,
1104*4882a593Smuzhiyun AR_PHY_CHAN_INFO_TAB_S2_READ,
1105*4882a593Smuzhiyun 0);
1106*4882a593Smuzhiyun
1107*4882a593Smuzhiyun /* 32 bits */
1108*4882a593Smuzhiyun iq_res[idx] = REG_READ(ah,
1109*4882a593Smuzhiyun chan_info_tab[i] +
1110*4882a593Smuzhiyun offset);
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun REG_RMW_FIELD(ah,
1113*4882a593Smuzhiyun AR_PHY_CHAN_INFO_MEMORY,
1114*4882a593Smuzhiyun AR_PHY_CHAN_INFO_TAB_S2_READ,
1115*4882a593Smuzhiyun 1);
1116*4882a593Smuzhiyun
1117*4882a593Smuzhiyun /* 16 bits */
1118*4882a593Smuzhiyun iq_res[idx + 1] = 0xffff & REG_READ(ah,
1119*4882a593Smuzhiyun chan_info_tab[i] + offset);
1120*4882a593Smuzhiyun
1121*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1122*4882a593Smuzhiyun "IQ_RES[%d]=0x%x IQ_RES[%d]=0x%x\n",
1123*4882a593Smuzhiyun idx, iq_res[idx], idx + 1,
1124*4882a593Smuzhiyun iq_res[idx + 1]);
1125*4882a593Smuzhiyun }
1126*4882a593Smuzhiyun
1127*4882a593Smuzhiyun if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
1128*4882a593Smuzhiyun coeff.iqc_coeff)) {
1129*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1130*4882a593Smuzhiyun "Failed in calculation of IQ correction\n");
1131*4882a593Smuzhiyun goto tx_iqcal_fail;
1132*4882a593Smuzhiyun }
1133*4882a593Smuzhiyun
1134*4882a593Smuzhiyun coeff.phs_coeff[i][im][iqcal_idx] =
1135*4882a593Smuzhiyun coeff.iqc_coeff[0] & 0x7f;
1136*4882a593Smuzhiyun coeff.mag_coeff[i][im][iqcal_idx] =
1137*4882a593Smuzhiyun (coeff.iqc_coeff[0] >> 7) & 0x7f;
1138*4882a593Smuzhiyun
1139*4882a593Smuzhiyun if (coeff.mag_coeff[i][im][iqcal_idx] > 63)
1140*4882a593Smuzhiyun coeff.mag_coeff[i][im][iqcal_idx] -= 128;
1141*4882a593Smuzhiyun if (coeff.phs_coeff[i][im][iqcal_idx] > 63)
1142*4882a593Smuzhiyun coeff.phs_coeff[i][im][iqcal_idx] -= 128;
1143*4882a593Smuzhiyun }
1144*4882a593Smuzhiyun }
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun if (AR_SREV_9550(ah))
1147*4882a593Smuzhiyun outlier_detect = ar955x_tx_iq_cal_median(ah, &coeff,
1148*4882a593Smuzhiyun iqcal_idx, nmeasurement);
1149*4882a593Smuzhiyun if (outlier_detect)
1150*4882a593Smuzhiyun ar9003_hw_tx_iq_cal_outlier_detection(ah, &coeff, is_reusable);
1151*4882a593Smuzhiyun
1152*4882a593Smuzhiyun return;
1153*4882a593Smuzhiyun
1154*4882a593Smuzhiyun tx_iqcal_fail:
1155*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "Tx IQ Cal failed\n");
1156*4882a593Smuzhiyun return;
1157*4882a593Smuzhiyun }
1158*4882a593Smuzhiyun
ar9003_hw_tx_iq_cal_reload(struct ath_hw * ah)1159*4882a593Smuzhiyun static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah)
1160*4882a593Smuzhiyun {
1161*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
1162*4882a593Smuzhiyun u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
1163*4882a593Smuzhiyun int i, im;
1164*4882a593Smuzhiyun
1165*4882a593Smuzhiyun memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff));
1166*4882a593Smuzhiyun for (i = 0; i < MAX_MEASUREMENT / 2; i++) {
1167*4882a593Smuzhiyun tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] =
1168*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B0(i);
1169*4882a593Smuzhiyun if (!AR_SREV_9485(ah)) {
1170*4882a593Smuzhiyun tx_corr_coeff[i * 2][1] =
1171*4882a593Smuzhiyun tx_corr_coeff[(i * 2) + 1][1] =
1172*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B1(i);
1173*4882a593Smuzhiyun
1174*4882a593Smuzhiyun tx_corr_coeff[i * 2][2] =
1175*4882a593Smuzhiyun tx_corr_coeff[(i * 2) + 1][2] =
1176*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_B2(i);
1177*4882a593Smuzhiyun }
1178*4882a593Smuzhiyun }
1179*4882a593Smuzhiyun
1180*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1181*4882a593Smuzhiyun if (!(ah->txchainmask & (1 << i)))
1182*4882a593Smuzhiyun continue;
1183*4882a593Smuzhiyun
1184*4882a593Smuzhiyun for (im = 0; im < caldata->num_measures[i]; im++) {
1185*4882a593Smuzhiyun if ((im % 2) == 0)
1186*4882a593Smuzhiyun REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
1187*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
1188*4882a593Smuzhiyun caldata->tx_corr_coeff[im][i]);
1189*4882a593Smuzhiyun else
1190*4882a593Smuzhiyun REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
1191*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
1192*4882a593Smuzhiyun caldata->tx_corr_coeff[im][i]);
1193*4882a593Smuzhiyun }
1194*4882a593Smuzhiyun }
1195*4882a593Smuzhiyun
1196*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
1197*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1);
1198*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
1199*4882a593Smuzhiyun AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
1200*4882a593Smuzhiyun }
1201*4882a593Smuzhiyun
ar9003_hw_manual_peak_cal(struct ath_hw * ah,u8 chain,bool is_2g)1202*4882a593Smuzhiyun static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
1203*4882a593Smuzhiyun {
1204*4882a593Smuzhiyun int offset[8] = {0}, total = 0, test;
1205*4882a593Smuzhiyun int agc_out, i, peak_detect_threshold = 0;
1206*4882a593Smuzhiyun
1207*4882a593Smuzhiyun if (AR_SREV_9550(ah) || AR_SREV_9531(ah))
1208*4882a593Smuzhiyun peak_detect_threshold = 8;
1209*4882a593Smuzhiyun else if (AR_SREV_9561(ah))
1210*4882a593Smuzhiyun peak_detect_threshold = 11;
1211*4882a593Smuzhiyun
1212*4882a593Smuzhiyun /*
1213*4882a593Smuzhiyun * Turn off LNA/SW.
1214*4882a593Smuzhiyun */
1215*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
1216*4882a593Smuzhiyun AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1);
1217*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
1218*4882a593Smuzhiyun AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0);
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9330_11(ah)) {
1221*4882a593Smuzhiyun if (is_2g)
1222*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
1223*4882a593Smuzhiyun AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0);
1224*4882a593Smuzhiyun else
1225*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
1226*4882a593Smuzhiyun AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0);
1227*4882a593Smuzhiyun }
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun /*
1230*4882a593Smuzhiyun * Turn off RXON.
1231*4882a593Smuzhiyun */
1232*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
1233*4882a593Smuzhiyun AR_PHY_65NM_RXTX2_RXON_OVR, 0x1);
1234*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
1235*4882a593Smuzhiyun AR_PHY_65NM_RXTX2_RXON, 0x0);
1236*4882a593Smuzhiyun
1237*4882a593Smuzhiyun /*
1238*4882a593Smuzhiyun * Turn on AGC for cal.
1239*4882a593Smuzhiyun */
1240*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1241*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
1242*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1243*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1);
1244*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1245*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1);
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun if (AR_SREV_9330_11(ah))
1248*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1249*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0);
1250*4882a593Smuzhiyun
1251*4882a593Smuzhiyun if (is_2g)
1252*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1253*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
1254*4882a593Smuzhiyun peak_detect_threshold);
1255*4882a593Smuzhiyun else
1256*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1257*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR,
1258*4882a593Smuzhiyun peak_detect_threshold);
1259*4882a593Smuzhiyun
1260*4882a593Smuzhiyun for (i = 6; i > 0; i--) {
1261*4882a593Smuzhiyun offset[i] = BIT(i - 1);
1262*4882a593Smuzhiyun test = total + offset[i];
1263*4882a593Smuzhiyun
1264*4882a593Smuzhiyun if (is_2g)
1265*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1266*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR,
1267*4882a593Smuzhiyun test);
1268*4882a593Smuzhiyun else
1269*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1270*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR,
1271*4882a593Smuzhiyun test);
1272*4882a593Smuzhiyun udelay(100);
1273*4882a593Smuzhiyun agc_out = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1274*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC_OUT);
1275*4882a593Smuzhiyun offset[i] = (agc_out) ? 0 : 1;
1276*4882a593Smuzhiyun total += (offset[i] << (i - 1));
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun
1279*4882a593Smuzhiyun if (is_2g)
1280*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1281*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, total);
1282*4882a593Smuzhiyun else
1283*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1284*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total);
1285*4882a593Smuzhiyun
1286*4882a593Smuzhiyun /*
1287*4882a593Smuzhiyun * Turn on LNA.
1288*4882a593Smuzhiyun */
1289*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain),
1290*4882a593Smuzhiyun AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0);
1291*4882a593Smuzhiyun /*
1292*4882a593Smuzhiyun * Turn off RXON.
1293*4882a593Smuzhiyun */
1294*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain),
1295*4882a593Smuzhiyun AR_PHY_65NM_RXTX2_RXON_OVR, 0);
1296*4882a593Smuzhiyun /*
1297*4882a593Smuzhiyun * Turn off peak detect calibration.
1298*4882a593Smuzhiyun */
1299*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
1300*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0);
1301*4882a593Smuzhiyun }
1302*4882a593Smuzhiyun
ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw * ah,struct ath9k_channel * chan,bool run_rtt_cal)1303*4882a593Smuzhiyun static void ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw *ah,
1304*4882a593Smuzhiyun struct ath9k_channel *chan,
1305*4882a593Smuzhiyun bool run_rtt_cal)
1306*4882a593Smuzhiyun {
1307*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
1308*4882a593Smuzhiyun int i;
1309*4882a593Smuzhiyun
1310*4882a593Smuzhiyun if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal)
1311*4882a593Smuzhiyun return;
1312*4882a593Smuzhiyun
1313*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1314*4882a593Smuzhiyun if (!(ah->rxchainmask & (1 << i)))
1315*4882a593Smuzhiyun continue;
1316*4882a593Smuzhiyun ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan));
1317*4882a593Smuzhiyun }
1318*4882a593Smuzhiyun
1319*4882a593Smuzhiyun if (caldata)
1320*4882a593Smuzhiyun set_bit(SW_PKDET_DONE, &caldata->cal_flags);
1321*4882a593Smuzhiyun
1322*4882a593Smuzhiyun if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && caldata) {
1323*4882a593Smuzhiyun if (IS_CHAN_2GHZ(chan)){
1324*4882a593Smuzhiyun caldata->caldac[0] = REG_READ_FIELD(ah,
1325*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC(0),
1326*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR);
1327*4882a593Smuzhiyun caldata->caldac[1] = REG_READ_FIELD(ah,
1328*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC(1),
1329*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR);
1330*4882a593Smuzhiyun } else {
1331*4882a593Smuzhiyun caldata->caldac[0] = REG_READ_FIELD(ah,
1332*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC(0),
1333*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR);
1334*4882a593Smuzhiyun caldata->caldac[1] = REG_READ_FIELD(ah,
1335*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC(1),
1336*4882a593Smuzhiyun AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR);
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun }
1339*4882a593Smuzhiyun }
1340*4882a593Smuzhiyun
ar9003_hw_cl_cal_post_proc(struct ath_hw * ah,bool is_reusable)1341*4882a593Smuzhiyun static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable)
1342*4882a593Smuzhiyun {
1343*4882a593Smuzhiyun u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0,
1344*4882a593Smuzhiyun AR_PHY_CL_TAB_1,
1345*4882a593Smuzhiyun AR_PHY_CL_TAB_2 };
1346*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
1347*4882a593Smuzhiyun bool txclcal_done = false;
1348*4882a593Smuzhiyun int i, j;
1349*4882a593Smuzhiyun
1350*4882a593Smuzhiyun if (!caldata || !(ah->enabled_cals & TX_CL_CAL))
1351*4882a593Smuzhiyun return;
1352*4882a593Smuzhiyun
1353*4882a593Smuzhiyun txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) &
1354*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CLC_SUCCESS);
1355*4882a593Smuzhiyun
1356*4882a593Smuzhiyun if (test_bit(TXCLCAL_DONE, &caldata->cal_flags)) {
1357*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1358*4882a593Smuzhiyun if (!(ah->txchainmask & (1 << i)))
1359*4882a593Smuzhiyun continue;
1360*4882a593Smuzhiyun for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
1361*4882a593Smuzhiyun REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]),
1362*4882a593Smuzhiyun caldata->tx_clcal[i][j]);
1363*4882a593Smuzhiyun }
1364*4882a593Smuzhiyun } else if (is_reusable && txclcal_done) {
1365*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1366*4882a593Smuzhiyun if (!(ah->txchainmask & (1 << i)))
1367*4882a593Smuzhiyun continue;
1368*4882a593Smuzhiyun for (j = 0; j < MAX_CL_TAB_ENTRY; j++)
1369*4882a593Smuzhiyun caldata->tx_clcal[i][j] =
1370*4882a593Smuzhiyun REG_READ(ah, CL_TAB_ENTRY(cl_idx[i]));
1371*4882a593Smuzhiyun }
1372*4882a593Smuzhiyun set_bit(TXCLCAL_DONE, &caldata->cal_flags);
1373*4882a593Smuzhiyun }
1374*4882a593Smuzhiyun }
1375*4882a593Smuzhiyun
ar9003_hw_init_cal_common(struct ath_hw * ah)1376*4882a593Smuzhiyun static void ar9003_hw_init_cal_common(struct ath_hw *ah)
1377*4882a593Smuzhiyun {
1378*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
1379*4882a593Smuzhiyun
1380*4882a593Smuzhiyun /* Initialize list pointers */
1381*4882a593Smuzhiyun ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
1382*4882a593Smuzhiyun
1383*4882a593Smuzhiyun INIT_CAL(&ah->iq_caldata);
1384*4882a593Smuzhiyun INSERT_CAL(ah, &ah->iq_caldata);
1385*4882a593Smuzhiyun
1386*4882a593Smuzhiyun /* Initialize current pointer to first element in list */
1387*4882a593Smuzhiyun ah->cal_list_curr = ah->cal_list;
1388*4882a593Smuzhiyun
1389*4882a593Smuzhiyun if (ah->cal_list_curr)
1390*4882a593Smuzhiyun ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
1391*4882a593Smuzhiyun
1392*4882a593Smuzhiyun if (caldata)
1393*4882a593Smuzhiyun caldata->CalValid = 0;
1394*4882a593Smuzhiyun }
1395*4882a593Smuzhiyun
ar9003_hw_init_cal_pcoem(struct ath_hw * ah,struct ath9k_channel * chan)1396*4882a593Smuzhiyun static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah,
1397*4882a593Smuzhiyun struct ath9k_channel *chan)
1398*4882a593Smuzhiyun {
1399*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
1400*4882a593Smuzhiyun struct ath9k_hw_cal_data *caldata = ah->caldata;
1401*4882a593Smuzhiyun bool txiqcal_done = false;
1402*4882a593Smuzhiyun bool is_reusable = true, status = true;
1403*4882a593Smuzhiyun bool run_rtt_cal = false, run_agc_cal;
1404*4882a593Smuzhiyun bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
1405*4882a593Smuzhiyun u32 rx_delay = 0;
1406*4882a593Smuzhiyun u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
1407*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_FLTR_CAL |
1408*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_PKDET_CAL;
1409*4882a593Smuzhiyun
1410*4882a593Smuzhiyun /* Use chip chainmask only for calibration */
1411*4882a593Smuzhiyun ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
1412*4882a593Smuzhiyun
1413*4882a593Smuzhiyun if (rtt) {
1414*4882a593Smuzhiyun if (!ar9003_hw_rtt_restore(ah, chan))
1415*4882a593Smuzhiyun run_rtt_cal = true;
1416*4882a593Smuzhiyun
1417*4882a593Smuzhiyun if (run_rtt_cal)
1418*4882a593Smuzhiyun ath_dbg(common, CALIBRATE, "RTT calibration to be done\n");
1419*4882a593Smuzhiyun }
1420*4882a593Smuzhiyun
1421*4882a593Smuzhiyun run_agc_cal = run_rtt_cal;
1422*4882a593Smuzhiyun
1423*4882a593Smuzhiyun if (run_rtt_cal) {
1424*4882a593Smuzhiyun ar9003_hw_rtt_enable(ah);
1425*4882a593Smuzhiyun ar9003_hw_rtt_set_mask(ah, 0x00);
1426*4882a593Smuzhiyun ar9003_hw_rtt_clear_hist(ah);
1427*4882a593Smuzhiyun }
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun if (rtt) {
1430*4882a593Smuzhiyun if (!run_rtt_cal) {
1431*4882a593Smuzhiyun agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL);
1432*4882a593Smuzhiyun agc_supp_cals &= agc_ctrl;
1433*4882a593Smuzhiyun agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL |
1434*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_FLTR_CAL |
1435*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_PKDET_CAL);
1436*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
1437*4882a593Smuzhiyun } else {
1438*4882a593Smuzhiyun if (ah->ah_flags & AH_FASTCC)
1439*4882a593Smuzhiyun run_agc_cal = true;
1440*4882a593Smuzhiyun }
1441*4882a593Smuzhiyun }
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun if (ah->enabled_cals & TX_CL_CAL) {
1444*4882a593Smuzhiyun if (caldata && test_bit(TXCLCAL_DONE, &caldata->cal_flags))
1445*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
1446*4882a593Smuzhiyun AR_PHY_CL_CAL_ENABLE);
1447*4882a593Smuzhiyun else {
1448*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL,
1449*4882a593Smuzhiyun AR_PHY_CL_CAL_ENABLE);
1450*4882a593Smuzhiyun run_agc_cal = true;
1451*4882a593Smuzhiyun }
1452*4882a593Smuzhiyun }
1453*4882a593Smuzhiyun
1454*4882a593Smuzhiyun if ((IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) ||
1455*4882a593Smuzhiyun !(ah->enabled_cals & TX_IQ_CAL))
1456*4882a593Smuzhiyun goto skip_tx_iqcal;
1457*4882a593Smuzhiyun
1458*4882a593Smuzhiyun /* Do Tx IQ Calibration */
1459*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
1460*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
1461*4882a593Smuzhiyun DELPT);
1462*4882a593Smuzhiyun
1463*4882a593Smuzhiyun /*
1464*4882a593Smuzhiyun * For AR9485 or later chips, TxIQ cal runs as part of
1465*4882a593Smuzhiyun * AGC calibration
1466*4882a593Smuzhiyun */
1467*4882a593Smuzhiyun if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
1468*4882a593Smuzhiyun if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags))
1469*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
1470*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
1471*4882a593Smuzhiyun else
1472*4882a593Smuzhiyun REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
1473*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
1474*4882a593Smuzhiyun txiqcal_done = run_agc_cal = true;
1475*4882a593Smuzhiyun }
1476*4882a593Smuzhiyun
1477*4882a593Smuzhiyun skip_tx_iqcal:
1478*4882a593Smuzhiyun if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
1479*4882a593Smuzhiyun ar9003_mci_init_cal_req(ah, &is_reusable);
1480*4882a593Smuzhiyun
1481*4882a593Smuzhiyun if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) {
1482*4882a593Smuzhiyun rx_delay = REG_READ(ah, AR_PHY_RX_DELAY);
1483*4882a593Smuzhiyun /* Disable BB_active */
1484*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
1485*4882a593Smuzhiyun udelay(5);
1486*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_RX_DELAY, AR_PHY_RX_DELAY_DELAY);
1487*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
1488*4882a593Smuzhiyun }
1489*4882a593Smuzhiyun
1490*4882a593Smuzhiyun if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
1491*4882a593Smuzhiyun /* Calibrate the AGC */
1492*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL,
1493*4882a593Smuzhiyun REG_READ(ah, AR_PHY_AGC_CONTROL) |
1494*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL);
1495*4882a593Smuzhiyun
1496*4882a593Smuzhiyun /* Poll for offset calibration complete */
1497*4882a593Smuzhiyun status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
1498*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL,
1499*4882a593Smuzhiyun 0, AH_WAIT_TIMEOUT);
1500*4882a593Smuzhiyun
1501*4882a593Smuzhiyun ar9003_hw_do_pcoem_manual_peak_cal(ah, chan, run_rtt_cal);
1502*4882a593Smuzhiyun }
1503*4882a593Smuzhiyun
1504*4882a593Smuzhiyun if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) {
1505*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_RX_DELAY, rx_delay);
1506*4882a593Smuzhiyun udelay(5);
1507*4882a593Smuzhiyun }
1508*4882a593Smuzhiyun
1509*4882a593Smuzhiyun if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
1510*4882a593Smuzhiyun ar9003_mci_init_cal_done(ah);
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun if (rtt && !run_rtt_cal) {
1513*4882a593Smuzhiyun agc_ctrl |= agc_supp_cals;
1514*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
1515*4882a593Smuzhiyun }
1516*4882a593Smuzhiyun
1517*4882a593Smuzhiyun if (!status) {
1518*4882a593Smuzhiyun if (run_rtt_cal)
1519*4882a593Smuzhiyun ar9003_hw_rtt_disable(ah);
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1522*4882a593Smuzhiyun "offset calibration failed to complete in %d ms; noisy environment?\n",
1523*4882a593Smuzhiyun AH_WAIT_TIMEOUT / 1000);
1524*4882a593Smuzhiyun return false;
1525*4882a593Smuzhiyun }
1526*4882a593Smuzhiyun
1527*4882a593Smuzhiyun if (txiqcal_done)
1528*4882a593Smuzhiyun ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable);
1529*4882a593Smuzhiyun else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
1530*4882a593Smuzhiyun ar9003_hw_tx_iq_cal_reload(ah);
1531*4882a593Smuzhiyun
1532*4882a593Smuzhiyun ar9003_hw_cl_cal_post_proc(ah, is_reusable);
1533*4882a593Smuzhiyun
1534*4882a593Smuzhiyun if (run_rtt_cal && caldata) {
1535*4882a593Smuzhiyun if (is_reusable) {
1536*4882a593Smuzhiyun if (!ath9k_hw_rfbus_req(ah)) {
1537*4882a593Smuzhiyun ath_err(ath9k_hw_common(ah),
1538*4882a593Smuzhiyun "Could not stop baseband\n");
1539*4882a593Smuzhiyun } else {
1540*4882a593Smuzhiyun ar9003_hw_rtt_fill_hist(ah);
1541*4882a593Smuzhiyun
1542*4882a593Smuzhiyun if (test_bit(SW_PKDET_DONE, &caldata->cal_flags))
1543*4882a593Smuzhiyun ar9003_hw_rtt_load_hist(ah);
1544*4882a593Smuzhiyun }
1545*4882a593Smuzhiyun
1546*4882a593Smuzhiyun ath9k_hw_rfbus_done(ah);
1547*4882a593Smuzhiyun }
1548*4882a593Smuzhiyun
1549*4882a593Smuzhiyun ar9003_hw_rtt_disable(ah);
1550*4882a593Smuzhiyun }
1551*4882a593Smuzhiyun
1552*4882a593Smuzhiyun /* Revert chainmask to runtime parameters */
1553*4882a593Smuzhiyun ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
1554*4882a593Smuzhiyun
1555*4882a593Smuzhiyun ar9003_hw_init_cal_common(ah);
1556*4882a593Smuzhiyun
1557*4882a593Smuzhiyun return true;
1558*4882a593Smuzhiyun }
1559*4882a593Smuzhiyun
do_ar9003_agc_cal(struct ath_hw * ah)1560*4882a593Smuzhiyun static bool do_ar9003_agc_cal(struct ath_hw *ah)
1561*4882a593Smuzhiyun {
1562*4882a593Smuzhiyun struct ath_common *common = ath9k_hw_common(ah);
1563*4882a593Smuzhiyun bool status;
1564*4882a593Smuzhiyun
1565*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_AGC_CONTROL,
1566*4882a593Smuzhiyun REG_READ(ah, AR_PHY_AGC_CONTROL) |
1567*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL);
1568*4882a593Smuzhiyun
1569*4882a593Smuzhiyun status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
1570*4882a593Smuzhiyun AR_PHY_AGC_CONTROL_CAL,
1571*4882a593Smuzhiyun 0, AH_WAIT_TIMEOUT);
1572*4882a593Smuzhiyun if (!status) {
1573*4882a593Smuzhiyun ath_dbg(common, CALIBRATE,
1574*4882a593Smuzhiyun "offset calibration failed to complete in %d ms,"
1575*4882a593Smuzhiyun "noisy environment?\n",
1576*4882a593Smuzhiyun AH_WAIT_TIMEOUT / 1000);
1577*4882a593Smuzhiyun return false;
1578*4882a593Smuzhiyun }
1579*4882a593Smuzhiyun
1580*4882a593Smuzhiyun return true;
1581*4882a593Smuzhiyun }
1582*4882a593Smuzhiyun
ar9003_hw_init_cal_soc(struct ath_hw * ah,struct ath9k_channel * chan)1583*4882a593Smuzhiyun static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
1584*4882a593Smuzhiyun struct ath9k_channel *chan)
1585*4882a593Smuzhiyun {
1586*4882a593Smuzhiyun bool txiqcal_done = false;
1587*4882a593Smuzhiyun bool status = true;
1588*4882a593Smuzhiyun bool run_agc_cal = false, sep_iq_cal = false;
1589*4882a593Smuzhiyun int i = 0;
1590*4882a593Smuzhiyun
1591*4882a593Smuzhiyun /* Use chip chainmask only for calibration */
1592*4882a593Smuzhiyun ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
1593*4882a593Smuzhiyun
1594*4882a593Smuzhiyun if (ah->enabled_cals & TX_CL_CAL) {
1595*4882a593Smuzhiyun REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
1596*4882a593Smuzhiyun run_agc_cal = true;
1597*4882a593Smuzhiyun }
1598*4882a593Smuzhiyun
1599*4882a593Smuzhiyun if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))
1600*4882a593Smuzhiyun goto skip_tx_iqcal;
1601*4882a593Smuzhiyun
1602*4882a593Smuzhiyun /* Do Tx IQ Calibration */
1603*4882a593Smuzhiyun REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
1604*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
1605*4882a593Smuzhiyun DELPT);
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun /*
1608*4882a593Smuzhiyun * For AR9485 or later chips, TxIQ cal runs as part of
1609*4882a593Smuzhiyun * AGC calibration. Specifically, AR9550 in SoC chips.
1610*4882a593Smuzhiyun */
1611*4882a593Smuzhiyun if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
1612*4882a593Smuzhiyun if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
1613*4882a593Smuzhiyun AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) {
1614*4882a593Smuzhiyun txiqcal_done = true;
1615*4882a593Smuzhiyun } else {
1616*4882a593Smuzhiyun txiqcal_done = false;
1617*4882a593Smuzhiyun }
1618*4882a593Smuzhiyun run_agc_cal = true;
1619*4882a593Smuzhiyun } else {
1620*4882a593Smuzhiyun sep_iq_cal = true;
1621*4882a593Smuzhiyun run_agc_cal = true;
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun /*
1625*4882a593Smuzhiyun * In the SoC family, this will run for AR9300, AR9331 and AR9340.
1626*4882a593Smuzhiyun */
1627*4882a593Smuzhiyun if (sep_iq_cal) {
1628*4882a593Smuzhiyun txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
1629*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
1630*4882a593Smuzhiyun udelay(5);
1631*4882a593Smuzhiyun REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
1632*4882a593Smuzhiyun }
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) {
1635*4882a593Smuzhiyun if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done))
1636*4882a593Smuzhiyun return false;
1637*4882a593Smuzhiyun }
1638*4882a593Smuzhiyun
1639*4882a593Smuzhiyun skip_tx_iqcal:
1640*4882a593Smuzhiyun if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
1641*4882a593Smuzhiyun for (i = 0; i < AR9300_MAX_CHAINS; i++) {
1642*4882a593Smuzhiyun if (!(ah->rxchainmask & (1 << i)))
1643*4882a593Smuzhiyun continue;
1644*4882a593Smuzhiyun
1645*4882a593Smuzhiyun ar9003_hw_manual_peak_cal(ah, i,
1646*4882a593Smuzhiyun IS_CHAN_2GHZ(chan));
1647*4882a593Smuzhiyun }
1648*4882a593Smuzhiyun
1649*4882a593Smuzhiyun /*
1650*4882a593Smuzhiyun * For non-AR9550 chips, we just trigger AGC calibration
1651*4882a593Smuzhiyun * in the HW, poll for completion and then process
1652*4882a593Smuzhiyun * the results.
1653*4882a593Smuzhiyun *
1654*4882a593Smuzhiyun * For AR955x, we run it multiple times and use
1655*4882a593Smuzhiyun * median IQ correction.
1656*4882a593Smuzhiyun */
1657*4882a593Smuzhiyun if (!AR_SREV_9550(ah)) {
1658*4882a593Smuzhiyun status = do_ar9003_agc_cal(ah);
1659*4882a593Smuzhiyun if (!status)
1660*4882a593Smuzhiyun return false;
1661*4882a593Smuzhiyun
1662*4882a593Smuzhiyun if (txiqcal_done)
1663*4882a593Smuzhiyun ar9003_hw_tx_iq_cal_post_proc(ah, 0, false);
1664*4882a593Smuzhiyun } else {
1665*4882a593Smuzhiyun if (!txiqcal_done) {
1666*4882a593Smuzhiyun status = do_ar9003_agc_cal(ah);
1667*4882a593Smuzhiyun if (!status)
1668*4882a593Smuzhiyun return false;
1669*4882a593Smuzhiyun } else {
1670*4882a593Smuzhiyun for (i = 0; i < MAXIQCAL; i++) {
1671*4882a593Smuzhiyun status = do_ar9003_agc_cal(ah);
1672*4882a593Smuzhiyun if (!status)
1673*4882a593Smuzhiyun return false;
1674*4882a593Smuzhiyun ar9003_hw_tx_iq_cal_post_proc(ah, i, false);
1675*4882a593Smuzhiyun }
1676*4882a593Smuzhiyun }
1677*4882a593Smuzhiyun }
1678*4882a593Smuzhiyun }
1679*4882a593Smuzhiyun
1680*4882a593Smuzhiyun /* Revert chainmask to runtime parameters */
1681*4882a593Smuzhiyun ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
1682*4882a593Smuzhiyun
1683*4882a593Smuzhiyun ar9003_hw_init_cal_common(ah);
1684*4882a593Smuzhiyun
1685*4882a593Smuzhiyun return true;
1686*4882a593Smuzhiyun }
1687*4882a593Smuzhiyun
ar9003_hw_attach_calib_ops(struct ath_hw * ah)1688*4882a593Smuzhiyun void ar9003_hw_attach_calib_ops(struct ath_hw *ah)
1689*4882a593Smuzhiyun {
1690*4882a593Smuzhiyun struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
1691*4882a593Smuzhiyun struct ath_hw_ops *ops = ath9k_hw_ops(ah);
1692*4882a593Smuzhiyun
1693*4882a593Smuzhiyun if (AR_SREV_9003_PCOEM(ah))
1694*4882a593Smuzhiyun priv_ops->init_cal = ar9003_hw_init_cal_pcoem;
1695*4882a593Smuzhiyun else
1696*4882a593Smuzhiyun priv_ops->init_cal = ar9003_hw_init_cal_soc;
1697*4882a593Smuzhiyun
1698*4882a593Smuzhiyun priv_ops->init_cal_settings = ar9003_hw_init_cal_settings;
1699*4882a593Smuzhiyun priv_ops->setup_calibration = ar9003_hw_setup_calibration;
1700*4882a593Smuzhiyun
1701*4882a593Smuzhiyun ops->calibrate = ar9003_hw_calibrate;
1702*4882a593Smuzhiyun }
1703