xref: /onnv-gate/usr/src/uts/common/io/arn/arn_calib.c (revision 9999:d5e89571de4e)
1*9999SWang.Lin@Sun.COM /*
2*9999SWang.Lin@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*9999SWang.Lin@Sun.COM  * Use is subject to license terms.
4*9999SWang.Lin@Sun.COM  */
5*9999SWang.Lin@Sun.COM 
6*9999SWang.Lin@Sun.COM /*
7*9999SWang.Lin@Sun.COM  * Copyright (c) 2008 Atheros Communications Inc.
8*9999SWang.Lin@Sun.COM  *
9*9999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
10*9999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
11*9999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
12*9999SWang.Lin@Sun.COM  *
13*9999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14*9999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15*9999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16*9999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17*9999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18*9999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19*9999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20*9999SWang.Lin@Sun.COM  */
21*9999SWang.Lin@Sun.COM 
22*9999SWang.Lin@Sun.COM #include "arn_core.h"
23*9999SWang.Lin@Sun.COM #include "arn_hw.h"
24*9999SWang.Lin@Sun.COM #include "arn_reg.h"
25*9999SWang.Lin@Sun.COM #include "arn_phy.h"
26*9999SWang.Lin@Sun.COM 
27*9999SWang.Lin@Sun.COM static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 };
28*9999SWang.Lin@Sun.COM 
29*9999SWang.Lin@Sun.COM /* We can tune this as we go by monitoring really low values */
30*9999SWang.Lin@Sun.COM #define	ATH9K_NF_TOO_LOW	-60
31*9999SWang.Lin@Sun.COM 
32*9999SWang.Lin@Sun.COM /*
33*9999SWang.Lin@Sun.COM  * AR5416 may return very high value (like -31 dBm), in those cases the nf
34*9999SWang.Lin@Sun.COM  * is incorrect and we should use the static NF value. Later we can try to
35*9999SWang.Lin@Sun.COM  * find out why they are reporting these values
36*9999SWang.Lin@Sun.COM  */
37*9999SWang.Lin@Sun.COM 
38*9999SWang.Lin@Sun.COM /* ARGSUSED */
39*9999SWang.Lin@Sun.COM static boolean_t
40*9999SWang.Lin@Sun.COM ath9k_hw_nf_in_range(struct ath_hal *ah, signed short nf)
41*9999SWang.Lin@Sun.COM {
42*9999SWang.Lin@Sun.COM 	if (nf > ATH9K_NF_TOO_LOW) {
43*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
44*9999SWang.Lin@Sun.COM 		    "%s: noise floor value detected (%d) is "
45*9999SWang.Lin@Sun.COM 		    "lower than what we think is a "
46*9999SWang.Lin@Sun.COM 		    "reasonable value (%d)\n",
47*9999SWang.Lin@Sun.COM 		    __func__, nf, ATH9K_NF_TOO_LOW));
48*9999SWang.Lin@Sun.COM 
49*9999SWang.Lin@Sun.COM 		return (B_FALSE);
50*9999SWang.Lin@Sun.COM 	}
51*9999SWang.Lin@Sun.COM 	return (B_TRUE);
52*9999SWang.Lin@Sun.COM }
53*9999SWang.Lin@Sun.COM 
54*9999SWang.Lin@Sun.COM static int16_t
55*9999SWang.Lin@Sun.COM ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
56*9999SWang.Lin@Sun.COM {
57*9999SWang.Lin@Sun.COM 	int16_t nfval;
58*9999SWang.Lin@Sun.COM 	int16_t sort[ATH9K_NF_CAL_HIST_MAX];
59*9999SWang.Lin@Sun.COM 	int i, j;
60*9999SWang.Lin@Sun.COM 
61*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
62*9999SWang.Lin@Sun.COM 		sort[i] = nfCalBuffer[i];
63*9999SWang.Lin@Sun.COM 
64*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
65*9999SWang.Lin@Sun.COM 		for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
66*9999SWang.Lin@Sun.COM 			if (sort[j] > sort[j - 1]) {
67*9999SWang.Lin@Sun.COM 				nfval = sort[j];
68*9999SWang.Lin@Sun.COM 				sort[j] = sort[j - 1];
69*9999SWang.Lin@Sun.COM 				sort[j - 1] = nfval;
70*9999SWang.Lin@Sun.COM 			}
71*9999SWang.Lin@Sun.COM 		}
72*9999SWang.Lin@Sun.COM 	}
73*9999SWang.Lin@Sun.COM 	nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
74*9999SWang.Lin@Sun.COM 
75*9999SWang.Lin@Sun.COM 	return (nfval);
76*9999SWang.Lin@Sun.COM }
77*9999SWang.Lin@Sun.COM 
78*9999SWang.Lin@Sun.COM static void
79*9999SWang.Lin@Sun.COM ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
80*9999SWang.Lin@Sun.COM     int16_t *nfarray)
81*9999SWang.Lin@Sun.COM {
82*9999SWang.Lin@Sun.COM 	int i;
83*9999SWang.Lin@Sun.COM 
84*9999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
85*9999SWang.Lin@Sun.COM 		h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
86*9999SWang.Lin@Sun.COM 
87*9999SWang.Lin@Sun.COM 		if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
88*9999SWang.Lin@Sun.COM 			h[i].currIndex = 0;
89*9999SWang.Lin@Sun.COM 
90*9999SWang.Lin@Sun.COM 		if (h[i].invalidNFcount > 0) {
91*9999SWang.Lin@Sun.COM 			if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE ||
92*9999SWang.Lin@Sun.COM 			    nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
93*9999SWang.Lin@Sun.COM 				h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
94*9999SWang.Lin@Sun.COM 			} else {
95*9999SWang.Lin@Sun.COM 				h[i].invalidNFcount--;
96*9999SWang.Lin@Sun.COM 				h[i].privNF = nfarray[i];
97*9999SWang.Lin@Sun.COM 			}
98*9999SWang.Lin@Sun.COM 		} else {
99*9999SWang.Lin@Sun.COM 			h[i].privNF =
100*9999SWang.Lin@Sun.COM 			    ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
101*9999SWang.Lin@Sun.COM 		}
102*9999SWang.Lin@Sun.COM 	}
103*9999SWang.Lin@Sun.COM }
104*9999SWang.Lin@Sun.COM 
105*9999SWang.Lin@Sun.COM static void
106*9999SWang.Lin@Sun.COM ath9k_hw_do_getnf(struct ath_hal *ah,
107*9999SWang.Lin@Sun.COM     int16_t nfarray[NUM_NF_READINGS])
108*9999SWang.Lin@Sun.COM {
109*9999SWang.Lin@Sun.COM 	int16_t nf;
110*9999SWang.Lin@Sun.COM 
111*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
112*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
113*9999SWang.Lin@Sun.COM 	else
114*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
115*9999SWang.Lin@Sun.COM 
116*9999SWang.Lin@Sun.COM 	if (nf & 0x100)
117*9999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
118*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
119*9999SWang.Lin@Sun.COM 	    "NF calibrated [ctl] [chain 0] is %d\n", nf));
120*9999SWang.Lin@Sun.COM 	nfarray[0] = nf;
121*9999SWang.Lin@Sun.COM 
122*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
123*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
124*9999SWang.Lin@Sun.COM 		    AR9280_PHY_CH1_MINCCA_PWR);
125*9999SWang.Lin@Sun.COM 	else
126*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
127*9999SWang.Lin@Sun.COM 		    AR_PHY_CH1_MINCCA_PWR);
128*9999SWang.Lin@Sun.COM 
129*9999SWang.Lin@Sun.COM 	if (nf & 0x100)
130*9999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
131*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
132*9999SWang.Lin@Sun.COM 	    "NF calibrated [ctl] [chain 1] is %d\n", nf));
133*9999SWang.Lin@Sun.COM 	nfarray[1] = nf;
134*9999SWang.Lin@Sun.COM 
135*9999SWang.Lin@Sun.COM 	if (!AR_SREV_9280(ah)) {
136*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH2_CCA),
137*9999SWang.Lin@Sun.COM 		    AR_PHY_CH2_MINCCA_PWR);
138*9999SWang.Lin@Sun.COM 		if (nf & 0x100)
139*9999SWang.Lin@Sun.COM 			nf = 0 - ((nf ^ 0x1ff) + 1);
140*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
141*9999SWang.Lin@Sun.COM 		    "NF calibrated [ctl] [chain 2] is %d\n", nf));
142*9999SWang.Lin@Sun.COM 		nfarray[2] = nf;
143*9999SWang.Lin@Sun.COM 	}
144*9999SWang.Lin@Sun.COM 
145*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
146*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
147*9999SWang.Lin@Sun.COM 		    AR9280_PHY_EXT_MINCCA_PWR);
148*9999SWang.Lin@Sun.COM 	else
149*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
150*9999SWang.Lin@Sun.COM 		    AR_PHY_EXT_MINCCA_PWR);
151*9999SWang.Lin@Sun.COM 
152*9999SWang.Lin@Sun.COM 	if (nf & 0x100)
153*9999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
154*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
155*9999SWang.Lin@Sun.COM 	    "NF calibrated [ext] [chain 0] is %d\n", nf));
156*9999SWang.Lin@Sun.COM 	nfarray[3] = nf;
157*9999SWang.Lin@Sun.COM 
158*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
159*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
160*9999SWang.Lin@Sun.COM 		    AR9280_PHY_CH1_EXT_MINCCA_PWR);
161*9999SWang.Lin@Sun.COM 	else
162*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
163*9999SWang.Lin@Sun.COM 		    AR_PHY_CH1_EXT_MINCCA_PWR);
164*9999SWang.Lin@Sun.COM 
165*9999SWang.Lin@Sun.COM 	if (nf & 0x100)
166*9999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
167*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
168*9999SWang.Lin@Sun.COM 	    "NF calibrated [ext] [chain 1] is %d\n", nf));
169*9999SWang.Lin@Sun.COM 	nfarray[4] = nf;
170*9999SWang.Lin@Sun.COM 
171*9999SWang.Lin@Sun.COM 	if (!AR_SREV_9280(ah)) {
172*9999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA),
173*9999SWang.Lin@Sun.COM 		    AR_PHY_CH2_EXT_MINCCA_PWR);
174*9999SWang.Lin@Sun.COM 		if (nf & 0x100)
175*9999SWang.Lin@Sun.COM 			nf = 0 - ((nf ^ 0x1ff) + 1);
176*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
177*9999SWang.Lin@Sun.COM 		    "NF calibrated [ext] [chain 2] is %d\n", nf));
178*9999SWang.Lin@Sun.COM 		nfarray[5] = nf;
179*9999SWang.Lin@Sun.COM 	}
180*9999SWang.Lin@Sun.COM }
181*9999SWang.Lin@Sun.COM 
182*9999SWang.Lin@Sun.COM static boolean_t
183*9999SWang.Lin@Sun.COM getNoiseFloorThresh(struct ath_hal *ah,
184*9999SWang.Lin@Sun.COM     const struct ath9k_channel *chan,
185*9999SWang.Lin@Sun.COM     int16_t *nft)
186*9999SWang.Lin@Sun.COM {
187*9999SWang.Lin@Sun.COM 	switch (chan->chanmode) {
188*9999SWang.Lin@Sun.COM 	case CHANNEL_A:
189*9999SWang.Lin@Sun.COM 	case CHANNEL_A_HT20:
190*9999SWang.Lin@Sun.COM 	case CHANNEL_A_HT40PLUS:
191*9999SWang.Lin@Sun.COM 	case CHANNEL_A_HT40MINUS:
192*9999SWang.Lin@Sun.COM 		*nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_5);
193*9999SWang.Lin@Sun.COM 		break;
194*9999SWang.Lin@Sun.COM 	case CHANNEL_B:
195*9999SWang.Lin@Sun.COM 	case CHANNEL_G:
196*9999SWang.Lin@Sun.COM 	case CHANNEL_G_HT20:
197*9999SWang.Lin@Sun.COM 	case CHANNEL_G_HT40PLUS:
198*9999SWang.Lin@Sun.COM 	case CHANNEL_G_HT40MINUS:
199*9999SWang.Lin@Sun.COM 		*nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_2);
200*9999SWang.Lin@Sun.COM 		break;
201*9999SWang.Lin@Sun.COM 	default:
202*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL,
203*9999SWang.Lin@Sun.COM 		    "%s: invalid channel flags 0x%x\n", __func__,
204*9999SWang.Lin@Sun.COM 		    chan->channelFlags));
205*9999SWang.Lin@Sun.COM 		return (B_FALSE);
206*9999SWang.Lin@Sun.COM 	}
207*9999SWang.Lin@Sun.COM 
208*9999SWang.Lin@Sun.COM 	return (B_TRUE);
209*9999SWang.Lin@Sun.COM }
210*9999SWang.Lin@Sun.COM 
211*9999SWang.Lin@Sun.COM static void
212*9999SWang.Lin@Sun.COM ath9k_hw_setup_calibration(struct ath_hal *ah,
213*9999SWang.Lin@Sun.COM     struct hal_cal_list *currCal)
214*9999SWang.Lin@Sun.COM {
215*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0),
216*9999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
217*9999SWang.Lin@Sun.COM 	    currCal->calData->calCountMax);
218*9999SWang.Lin@Sun.COM 
219*9999SWang.Lin@Sun.COM 	switch (currCal->calData->calType) {
220*9999SWang.Lin@Sun.COM 	case IQ_MISMATCH_CAL:
221*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
222*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
223*9999SWang.Lin@Sun.COM 		    "%s: starting IQ Mismatch Calibration\n",
224*9999SWang.Lin@Sun.COM 		    __func__));
225*9999SWang.Lin@Sun.COM 		break;
226*9999SWang.Lin@Sun.COM 	case ADC_GAIN_CAL:
227*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
228*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
229*9999SWang.Lin@Sun.COM 		    "%s: starting ADC Gain Calibration\n", __func__));
230*9999SWang.Lin@Sun.COM 		break;
231*9999SWang.Lin@Sun.COM 	case ADC_DC_CAL:
232*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
233*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
234*9999SWang.Lin@Sun.COM 		    "%s: starting ADC DC Calibration\n", __func__));
235*9999SWang.Lin@Sun.COM 		break;
236*9999SWang.Lin@Sun.COM 	case ADC_DC_INIT_CAL:
237*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
238*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
239*9999SWang.Lin@Sun.COM 		    "%s: starting Init ADC DC Calibration\n",
240*9999SWang.Lin@Sun.COM 		    __func__));
241*9999SWang.Lin@Sun.COM 		break;
242*9999SWang.Lin@Sun.COM 	}
243*9999SWang.Lin@Sun.COM 
244*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
245*9999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_DO_CAL);
246*9999SWang.Lin@Sun.COM }
247*9999SWang.Lin@Sun.COM 
248*9999SWang.Lin@Sun.COM static void
249*9999SWang.Lin@Sun.COM ath9k_hw_reset_calibration(struct ath_hal *ah,
250*9999SWang.Lin@Sun.COM     struct hal_cal_list *currCal)
251*9999SWang.Lin@Sun.COM {
252*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
253*9999SWang.Lin@Sun.COM 	int i;
254*9999SWang.Lin@Sun.COM 
255*9999SWang.Lin@Sun.COM 	ath9k_hw_setup_calibration(ah, currCal);
256*9999SWang.Lin@Sun.COM 
257*9999SWang.Lin@Sun.COM 	currCal->calState = CAL_RUNNING;
258*9999SWang.Lin@Sun.COM 
259*9999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
260*9999SWang.Lin@Sun.COM 		ahp->ah_Meas0.sign[i] = 0;
261*9999SWang.Lin@Sun.COM 		ahp->ah_Meas1.sign[i] = 0;
262*9999SWang.Lin@Sun.COM 		ahp->ah_Meas2.sign[i] = 0;
263*9999SWang.Lin@Sun.COM 		ahp->ah_Meas3.sign[i] = 0;
264*9999SWang.Lin@Sun.COM 	}
265*9999SWang.Lin@Sun.COM 
266*9999SWang.Lin@Sun.COM 	ahp->ah_CalSamples = 0;
267*9999SWang.Lin@Sun.COM }
268*9999SWang.Lin@Sun.COM 
269*9999SWang.Lin@Sun.COM static void
270*9999SWang.Lin@Sun.COM ath9k_hw_per_calibration(struct ath_hal *ah,
271*9999SWang.Lin@Sun.COM     struct ath9k_channel *ichan,
272*9999SWang.Lin@Sun.COM     uint8_t rxchainmask,
273*9999SWang.Lin@Sun.COM     struct hal_cal_list *currCal,
274*9999SWang.Lin@Sun.COM     boolean_t *isCalDone)
275*9999SWang.Lin@Sun.COM {
276*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
277*9999SWang.Lin@Sun.COM 
278*9999SWang.Lin@Sun.COM 	*isCalDone = B_FALSE;
279*9999SWang.Lin@Sun.COM 
280*9999SWang.Lin@Sun.COM 	if (currCal->calState == CAL_RUNNING) {
281*9999SWang.Lin@Sun.COM 		if (!(REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
282*9999SWang.Lin@Sun.COM 		    AR_PHY_TIMING_CTRL4_DO_CAL)) {
283*9999SWang.Lin@Sun.COM 
284*9999SWang.Lin@Sun.COM 			currCal->calData->calCollect(ah);
285*9999SWang.Lin@Sun.COM 			ahp->ah_CalSamples++;
286*9999SWang.Lin@Sun.COM 
287*9999SWang.Lin@Sun.COM 			if (ahp->ah_CalSamples >=
288*9999SWang.Lin@Sun.COM 			    currCal->calData->calNumSamples) {
289*9999SWang.Lin@Sun.COM 				int i, numChains = 0;
290*9999SWang.Lin@Sun.COM 				for (i = 0; i < AR5416_MAX_CHAINS; i++) {
291*9999SWang.Lin@Sun.COM 					if (rxchainmask & (1 << i))
292*9999SWang.Lin@Sun.COM 						numChains++;
293*9999SWang.Lin@Sun.COM 				}
294*9999SWang.Lin@Sun.COM 
295*9999SWang.Lin@Sun.COM 				currCal->calData->calPostProc(ah, numChains);
296*9999SWang.Lin@Sun.COM 				ichan->CalValid |= currCal->calData->calType;
297*9999SWang.Lin@Sun.COM 				currCal->calState = CAL_DONE;
298*9999SWang.Lin@Sun.COM 				*isCalDone = B_TRUE;
299*9999SWang.Lin@Sun.COM 			} else {
300*9999SWang.Lin@Sun.COM 				ath9k_hw_setup_calibration(ah, currCal);
301*9999SWang.Lin@Sun.COM 			}
302*9999SWang.Lin@Sun.COM 		}
303*9999SWang.Lin@Sun.COM 	} else if (!(ichan->CalValid & currCal->calData->calType)) {
304*9999SWang.Lin@Sun.COM 		ath9k_hw_reset_calibration(ah, currCal);
305*9999SWang.Lin@Sun.COM 	}
306*9999SWang.Lin@Sun.COM }
307*9999SWang.Lin@Sun.COM 
308*9999SWang.Lin@Sun.COM static boolean_t
309*9999SWang.Lin@Sun.COM ath9k_hw_iscal_supported(struct ath_hal *ah,
310*9999SWang.Lin@Sun.COM     struct ath9k_channel *chan,
311*9999SWang.Lin@Sun.COM     enum hal_cal_types calType)
312*9999SWang.Lin@Sun.COM {
313*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
314*9999SWang.Lin@Sun.COM 	boolean_t retval = B_FALSE;
315*9999SWang.Lin@Sun.COM 
316*9999SWang.Lin@Sun.COM 	switch (calType & ahp->ah_suppCals) {
317*9999SWang.Lin@Sun.COM 	case IQ_MISMATCH_CAL:
318*9999SWang.Lin@Sun.COM 		if (!IS_CHAN_B(chan))
319*9999SWang.Lin@Sun.COM 			retval = B_TRUE;
320*9999SWang.Lin@Sun.COM 		break;
321*9999SWang.Lin@Sun.COM 	case ADC_GAIN_CAL:
322*9999SWang.Lin@Sun.COM 	case ADC_DC_CAL:
323*9999SWang.Lin@Sun.COM 		if (!IS_CHAN_B(chan) &&
324*9999SWang.Lin@Sun.COM 		    !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan)))
325*9999SWang.Lin@Sun.COM 			retval = B_TRUE;
326*9999SWang.Lin@Sun.COM 		break;
327*9999SWang.Lin@Sun.COM 	}
328*9999SWang.Lin@Sun.COM 
329*9999SWang.Lin@Sun.COM 	return (retval);
330*9999SWang.Lin@Sun.COM }
331*9999SWang.Lin@Sun.COM 
332*9999SWang.Lin@Sun.COM static void
333*9999SWang.Lin@Sun.COM ath9k_hw_iqcal_collect(struct ath_hal *ah)
334*9999SWang.Lin@Sun.COM {
335*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
336*9999SWang.Lin@Sun.COM 	int i;
337*9999SWang.Lin@Sun.COM 
338*9999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
339*9999SWang.Lin@Sun.COM 		ahp->ah_totalPowerMeasI[i] +=
340*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
341*9999SWang.Lin@Sun.COM 		ahp->ah_totalPowerMeasQ[i] +=
342*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
343*9999SWang.Lin@Sun.COM 		ahp->ah_totalIqCorrMeas[i] +=
344*9999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
345*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
346*9999SWang.Lin@Sun.COM 		    "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
347*9999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i],
348*9999SWang.Lin@Sun.COM 		    ahp->ah_totalPowerMeasQ[i],
349*9999SWang.Lin@Sun.COM 		    ahp->ah_totalIqCorrMeas[i]));
350*9999SWang.Lin@Sun.COM 	}
351*9999SWang.Lin@Sun.COM }
352*9999SWang.Lin@Sun.COM 
353*9999SWang.Lin@Sun.COM static void
354*9999SWang.Lin@Sun.COM ath9k_hw_adc_gaincal_collect(struct ath_hal *ah)
355*9999SWang.Lin@Sun.COM {
356*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
357*9999SWang.Lin@Sun.COM 	int i;
358*9999SWang.Lin@Sun.COM 
359*9999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
360*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcIOddPhase[i] +=
361*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
362*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcIEvenPhase[i] +=
363*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
364*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcQOddPhase[i] +=
365*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
366*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcQEvenPhase[i] +=
367*9999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
368*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
369*9999SWang.Lin@Sun.COM 		    "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
370*9999SWang.Lin@Sun.COM 		    "oddq=0x%08x; evenq=0x%08x;\n",
371*9999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i,
372*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcIOddPhase[i],
373*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcIEvenPhase[i],
374*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcQOddPhase[i],
375*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcQEvenPhase[i]));
376*9999SWang.Lin@Sun.COM 	}
377*9999SWang.Lin@Sun.COM }
378*9999SWang.Lin@Sun.COM 
379*9999SWang.Lin@Sun.COM static void
380*9999SWang.Lin@Sun.COM ath9k_hw_adc_dccal_collect(struct ath_hal *ah)
381*9999SWang.Lin@Sun.COM {
382*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
383*9999SWang.Lin@Sun.COM 	int i;
384*9999SWang.Lin@Sun.COM 
385*9999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
386*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetIOddPhase[i] +=
387*9999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
388*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetIEvenPhase[i] +=
389*9999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
390*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetQOddPhase[i] +=
391*9999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
392*9999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetQEvenPhase[i] +=
393*9999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
394*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
395*9999SWang.Lin@Sun.COM 		    "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
396*9999SWang.Lin@Sun.COM 		    "oddq=0x%08x; evenq=0x%08x;\n",
397*9999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i,
398*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetIOddPhase[i],
399*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetIEvenPhase[i],
400*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetQOddPhase[i],
401*9999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetQEvenPhase[i]));
402*9999SWang.Lin@Sun.COM 	}
403*9999SWang.Lin@Sun.COM }
404*9999SWang.Lin@Sun.COM 
405*9999SWang.Lin@Sun.COM static void
406*9999SWang.Lin@Sun.COM ath9k_hw_iqcalibrate(struct ath_hal *ah, uint8_t numChains)
407*9999SWang.Lin@Sun.COM {
408*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
409*9999SWang.Lin@Sun.COM 	uint32_t powerMeasQ, powerMeasI, iqCorrMeas;
410*9999SWang.Lin@Sun.COM 	uint32_t qCoffDenom, iCoffDenom;
411*9999SWang.Lin@Sun.COM 	int32_t qCoff, iCoff;
412*9999SWang.Lin@Sun.COM 	int iqCorrNeg, i;
413*9999SWang.Lin@Sun.COM 
414*9999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
415*9999SWang.Lin@Sun.COM 		powerMeasI = ahp->ah_totalPowerMeasI[i];
416*9999SWang.Lin@Sun.COM 		powerMeasQ = ahp->ah_totalPowerMeasQ[i];
417*9999SWang.Lin@Sun.COM 		iqCorrMeas = ahp->ah_totalIqCorrMeas[i];
418*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
419*9999SWang.Lin@Sun.COM 		    "Starting IQ Cal and Correction for Chain %d\n",
420*9999SWang.Lin@Sun.COM 		    i));
421*9999SWang.Lin@Sun.COM 
422*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
423*9999SWang.Lin@Sun.COM 		    "Orignal: Chn %diq_corr_meas = 0x%08x\n",
424*9999SWang.Lin@Sun.COM 		    i, ahp->ah_totalIqCorrMeas[i]));
425*9999SWang.Lin@Sun.COM 
426*9999SWang.Lin@Sun.COM 		iqCorrNeg = 0;
427*9999SWang.Lin@Sun.COM 
428*9999SWang.Lin@Sun.COM 		if (iqCorrMeas > 0x80000000) {
429*9999SWang.Lin@Sun.COM 			iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
430*9999SWang.Lin@Sun.COM 			iqCorrNeg = 1;
431*9999SWang.Lin@Sun.COM 		}
432*9999SWang.Lin@Sun.COM 
433*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
434*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI));
435*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
436*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ));
437*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
438*9999SWang.Lin@Sun.COM 		    iqCorrNeg));
439*9999SWang.Lin@Sun.COM 
440*9999SWang.Lin@Sun.COM 		iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
441*9999SWang.Lin@Sun.COM 		qCoffDenom = powerMeasQ / 64;
442*9999SWang.Lin@Sun.COM 
443*9999SWang.Lin@Sun.COM 		if (powerMeasQ != 0) {
444*9999SWang.Lin@Sun.COM 			iCoff = iqCorrMeas / iCoffDenom;
445*9999SWang.Lin@Sun.COM 			qCoff = powerMeasI / qCoffDenom - 64;
446*9999SWang.Lin@Sun.COM 
447*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
448*9999SWang.Lin@Sun.COM 			    "Chn %d iCoff = 0x%08x\n", i, iCoff));
449*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
450*9999SWang.Lin@Sun.COM 			    "Chn %d qCoff = 0x%08x\n", i, qCoff));
451*9999SWang.Lin@Sun.COM 
452*9999SWang.Lin@Sun.COM 			iCoff = iCoff & 0x3f;
453*9999SWang.Lin@Sun.COM 
454*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
455*9999SWang.Lin@Sun.COM 			    "New: Chn %d iCoff = 0x%08x\n", i, iCoff));
456*9999SWang.Lin@Sun.COM 
457*9999SWang.Lin@Sun.COM 			if (iqCorrNeg == 0x0)
458*9999SWang.Lin@Sun.COM 				iCoff = 0x40 - iCoff;
459*9999SWang.Lin@Sun.COM 
460*9999SWang.Lin@Sun.COM 			if (qCoff > 15)
461*9999SWang.Lin@Sun.COM 				qCoff = 15;
462*9999SWang.Lin@Sun.COM 			else if (qCoff <= -16)
463*9999SWang.Lin@Sun.COM 				qCoff = 16;
464*9999SWang.Lin@Sun.COM 
465*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
466*9999SWang.Lin@Sun.COM 			    "Chn %d : iCoff = 0x%x  qCoff = 0x%x\n",
467*9999SWang.Lin@Sun.COM 			    i, iCoff, qCoff));
468*9999SWang.Lin@Sun.COM 
469*9999SWang.Lin@Sun.COM 			REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
470*9999SWang.Lin@Sun.COM 			    AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
471*9999SWang.Lin@Sun.COM 			    iCoff);
472*9999SWang.Lin@Sun.COM 			REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
473*9999SWang.Lin@Sun.COM 			    AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
474*9999SWang.Lin@Sun.COM 			    qCoff);
475*9999SWang.Lin@Sun.COM 
476*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
477*9999SWang.Lin@Sun.COM 			    "IQ Cal and Correction done for Chain %d\n",
478*9999SWang.Lin@Sun.COM 			    i));
479*9999SWang.Lin@Sun.COM 		}
480*9999SWang.Lin@Sun.COM 	}
481*9999SWang.Lin@Sun.COM 
482*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
483*9999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
484*9999SWang.Lin@Sun.COM }
485*9999SWang.Lin@Sun.COM 
486*9999SWang.Lin@Sun.COM static void
487*9999SWang.Lin@Sun.COM ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, uint8_t numChains)
488*9999SWang.Lin@Sun.COM {
489*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
490*9999SWang.Lin@Sun.COM 	uint32_t iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset,
491*9999SWang.Lin@Sun.COM 	    qEvenMeasOffset;
492*9999SWang.Lin@Sun.COM 	uint32_t qGainMismatch, iGainMismatch, val, i;
493*9999SWang.Lin@Sun.COM 
494*9999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
495*9999SWang.Lin@Sun.COM 		iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i];
496*9999SWang.Lin@Sun.COM 		iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i];
497*9999SWang.Lin@Sun.COM 		qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i];
498*9999SWang.Lin@Sun.COM 		qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i];
499*9999SWang.Lin@Sun.COM 
500*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
501*9999SWang.Lin@Sun.COM 		    "Starting ADC Gain Cal for Chain %d\n", i));
502*9999SWang.Lin@Sun.COM 
503*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
504*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
505*9999SWang.Lin@Sun.COM 		    iOddMeasOffset));
506*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
507*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_i = 0x%08x\n", i,
508*9999SWang.Lin@Sun.COM 		    iEvenMeasOffset));
509*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
510*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
511*9999SWang.Lin@Sun.COM 		    qOddMeasOffset));
512*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
513*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_q = 0x%08x\n", i,
514*9999SWang.Lin@Sun.COM 		    qEvenMeasOffset));
515*9999SWang.Lin@Sun.COM 
516*9999SWang.Lin@Sun.COM 		if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) {
517*9999SWang.Lin@Sun.COM 			iGainMismatch =
518*9999SWang.Lin@Sun.COM 			    ((iEvenMeasOffset * 32) /
519*9999SWang.Lin@Sun.COM 			    iOddMeasOffset) & 0x3f;
520*9999SWang.Lin@Sun.COM 			qGainMismatch =
521*9999SWang.Lin@Sun.COM 			    ((qOddMeasOffset * 32) /
522*9999SWang.Lin@Sun.COM 			    qEvenMeasOffset) & 0x3f;
523*9999SWang.Lin@Sun.COM 
524*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
525*9999SWang.Lin@Sun.COM 			    "Chn %d gain_mismatch_i = 0x%08x\n", i,
526*9999SWang.Lin@Sun.COM 			    iGainMismatch));
527*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
528*9999SWang.Lin@Sun.COM 			    "Chn %d gain_mismatch_q = 0x%08x\n", i,
529*9999SWang.Lin@Sun.COM 			    qGainMismatch));
530*9999SWang.Lin@Sun.COM 
531*9999SWang.Lin@Sun.COM 			val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
532*9999SWang.Lin@Sun.COM 			val &= 0xfffff000;
533*9999SWang.Lin@Sun.COM 			val |= (qGainMismatch) | (iGainMismatch << 6);
534*9999SWang.Lin@Sun.COM 			REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
535*9999SWang.Lin@Sun.COM 
536*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
537*9999SWang.Lin@Sun.COM 			    "ADC Gain Cal done for Chain %d\n", i));
538*9999SWang.Lin@Sun.COM 		}
539*9999SWang.Lin@Sun.COM 	}
540*9999SWang.Lin@Sun.COM 
541*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
542*9999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
543*9999SWang.Lin@Sun.COM 	    AR_PHY_NEW_ADC_GAIN_CORR_ENABLE);
544*9999SWang.Lin@Sun.COM }
545*9999SWang.Lin@Sun.COM 
546*9999SWang.Lin@Sun.COM static void
547*9999SWang.Lin@Sun.COM ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, uint8_t numChains)
548*9999SWang.Lin@Sun.COM {
549*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
550*9999SWang.Lin@Sun.COM 	uint32_t iOddMeasOffset, iEvenMeasOffset, val, i;
551*9999SWang.Lin@Sun.COM 	int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch;
552*9999SWang.Lin@Sun.COM 	const struct hal_percal_data *calData =
553*9999SWang.Lin@Sun.COM 	    ahp->ah_cal_list_curr->calData;
554*9999SWang.Lin@Sun.COM 	uint32_t numSamples =
555*9999SWang.Lin@Sun.COM 	    (1 << (calData->calCountMax + 5)) * calData->calNumSamples;
556*9999SWang.Lin@Sun.COM 
557*9999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
558*9999SWang.Lin@Sun.COM 		iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i];
559*9999SWang.Lin@Sun.COM 		iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i];
560*9999SWang.Lin@Sun.COM 		qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i];
561*9999SWang.Lin@Sun.COM 		qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i];
562*9999SWang.Lin@Sun.COM 
563*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
564*9999SWang.Lin@Sun.COM 		    "Starting ADC DC Offset Cal for Chain %d\n", i));
565*9999SWang.Lin@Sun.COM 
566*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
567*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_i = %d\n", i,
568*9999SWang.Lin@Sun.COM 		    iOddMeasOffset));
569*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
570*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_i = %d\n", i,
571*9999SWang.Lin@Sun.COM 		    iEvenMeasOffset));
572*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
573*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_q = %d\n", i,
574*9999SWang.Lin@Sun.COM 		    qOddMeasOffset));
575*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
576*9999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_q = %d\n", i,
577*9999SWang.Lin@Sun.COM 		    qEvenMeasOffset));
578*9999SWang.Lin@Sun.COM 
579*9999SWang.Lin@Sun.COM 		iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) /
580*9999SWang.Lin@Sun.COM 		    numSamples) & 0x1ff;
581*9999SWang.Lin@Sun.COM 		qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) /
582*9999SWang.Lin@Sun.COM 		    numSamples) & 0x1ff;
583*9999SWang.Lin@Sun.COM 
584*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
585*9999SWang.Lin@Sun.COM 		    "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
586*9999SWang.Lin@Sun.COM 		    iDcMismatch));
587*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
588*9999SWang.Lin@Sun.COM 		    "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
589*9999SWang.Lin@Sun.COM 		    qDcMismatch));
590*9999SWang.Lin@Sun.COM 
591*9999SWang.Lin@Sun.COM 		val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
592*9999SWang.Lin@Sun.COM 		val &= 0xc0000fff;
593*9999SWang.Lin@Sun.COM 		val |= (qDcMismatch << 12) | (iDcMismatch << 21);
594*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
595*9999SWang.Lin@Sun.COM 
596*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
597*9999SWang.Lin@Sun.COM 		    "ADC DC Offset Cal done for Chain %d\n", i));
598*9999SWang.Lin@Sun.COM 	}
599*9999SWang.Lin@Sun.COM 
600*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
601*9999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
602*9999SWang.Lin@Sun.COM 	    AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE);
603*9999SWang.Lin@Sun.COM }
604*9999SWang.Lin@Sun.COM 
605*9999SWang.Lin@Sun.COM void
606*9999SWang.Lin@Sun.COM ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
607*9999SWang.Lin@Sun.COM     boolean_t *isCalDone)
608*9999SWang.Lin@Sun.COM {
609*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
610*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan =
611*9999SWang.Lin@Sun.COM 	    ath9k_regd_check_channel(ah, chan);
612*9999SWang.Lin@Sun.COM 	struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
613*9999SWang.Lin@Sun.COM 
614*9999SWang.Lin@Sun.COM 	*isCalDone = B_TRUE;
615*9999SWang.Lin@Sun.COM 
616*9999SWang.Lin@Sun.COM 	if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
617*9999SWang.Lin@Sun.COM 		return;
618*9999SWang.Lin@Sun.COM 
619*9999SWang.Lin@Sun.COM 	if (currCal == NULL)
620*9999SWang.Lin@Sun.COM 		return;
621*9999SWang.Lin@Sun.COM 
622*9999SWang.Lin@Sun.COM 	if (ichan == NULL) {
623*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
624*9999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
625*9999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
626*9999SWang.Lin@Sun.COM 		return;
627*9999SWang.Lin@Sun.COM 	}
628*9999SWang.Lin@Sun.COM 
629*9999SWang.Lin@Sun.COM 
630*9999SWang.Lin@Sun.COM 	if (currCal->calState != CAL_DONE) {
631*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
632*9999SWang.Lin@Sun.COM 		    "%s: Calibration state incorrect, %d\n",
633*9999SWang.Lin@Sun.COM 		    __func__, currCal->calState));
634*9999SWang.Lin@Sun.COM 		return;
635*9999SWang.Lin@Sun.COM 	}
636*9999SWang.Lin@Sun.COM 
637*9999SWang.Lin@Sun.COM 
638*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType))
639*9999SWang.Lin@Sun.COM 		return;
640*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
641*9999SWang.Lin@Sun.COM 	    "%s: Resetting Cal %d state for channel %u/0x%x\n",
642*9999SWang.Lin@Sun.COM 	    __func__, currCal->calData->calType, chan->channel,
643*9999SWang.Lin@Sun.COM 	    chan->channelFlags));
644*9999SWang.Lin@Sun.COM 
645*9999SWang.Lin@Sun.COM 	ichan->CalValid &= ~currCal->calData->calType;
646*9999SWang.Lin@Sun.COM 	currCal->calState = CAL_WAITING;
647*9999SWang.Lin@Sun.COM 
648*9999SWang.Lin@Sun.COM 	*isCalDone = B_FALSE;
649*9999SWang.Lin@Sun.COM }
650*9999SWang.Lin@Sun.COM 
651*9999SWang.Lin@Sun.COM void
652*9999SWang.Lin@Sun.COM ath9k_hw_start_nfcal(struct ath_hal *ah)
653*9999SWang.Lin@Sun.COM {
654*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
655*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_ENABLE_NF);
656*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
657*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
658*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
659*9999SWang.Lin@Sun.COM }
660*9999SWang.Lin@Sun.COM 
661*9999SWang.Lin@Sun.COM /* ARGSUSED */
662*9999SWang.Lin@Sun.COM void
663*9999SWang.Lin@Sun.COM ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
664*9999SWang.Lin@Sun.COM {
665*9999SWang.Lin@Sun.COM 	struct ath9k_nfcal_hist *h;
666*9999SWang.Lin@Sun.COM 	int i, j;
667*9999SWang.Lin@Sun.COM 	int32_t val;
668*9999SWang.Lin@Sun.COM 	const uint32_t ar5416_cca_regs[6] = {
669*9999SWang.Lin@Sun.COM 		AR_PHY_CCA,
670*9999SWang.Lin@Sun.COM 		AR_PHY_CH1_CCA,
671*9999SWang.Lin@Sun.COM 		AR_PHY_CH2_CCA,
672*9999SWang.Lin@Sun.COM 		AR_PHY_EXT_CCA,
673*9999SWang.Lin@Sun.COM 		AR_PHY_CH1_EXT_CCA,
674*9999SWang.Lin@Sun.COM 		AR_PHY_CH2_EXT_CCA
675*9999SWang.Lin@Sun.COM 	};
676*9999SWang.Lin@Sun.COM 	uint8_t chainmask;
677*9999SWang.Lin@Sun.COM 
678*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280(ah))
679*9999SWang.Lin@Sun.COM 		chainmask = 0x1B;
680*9999SWang.Lin@Sun.COM 	else
681*9999SWang.Lin@Sun.COM 		chainmask = 0x3F;
682*9999SWang.Lin@Sun.COM 
683*9999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
684*9999SWang.Lin@Sun.COM 	h = chan->nfCalHist;
685*9999SWang.Lin@Sun.COM #else
686*9999SWang.Lin@Sun.COM 	h = ah->nfCalHist;
687*9999SWang.Lin@Sun.COM #endif
688*9999SWang.Lin@Sun.COM 
689*9999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
690*9999SWang.Lin@Sun.COM 		if (chainmask & (1 << i)) {
691*9999SWang.Lin@Sun.COM 			val = REG_READ(ah, ar5416_cca_regs[i]);
692*9999SWang.Lin@Sun.COM 			val &= 0xFFFFFE00;
693*9999SWang.Lin@Sun.COM 			val |= (((uint32_t)(h[i].privNF) << 1) & 0x1ff);
694*9999SWang.Lin@Sun.COM 			REG_WRITE(ah, ar5416_cca_regs[i], val);
695*9999SWang.Lin@Sun.COM 		}
696*9999SWang.Lin@Sun.COM 	}
697*9999SWang.Lin@Sun.COM 
698*9999SWang.Lin@Sun.COM 	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
699*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_ENABLE_NF);
700*9999SWang.Lin@Sun.COM 	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
701*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
702*9999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
703*9999SWang.Lin@Sun.COM 
704*9999SWang.Lin@Sun.COM 	for (j = 0; j < 1000; j++) {
705*9999SWang.Lin@Sun.COM 		if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
706*9999SWang.Lin@Sun.COM 		    AR_PHY_AGC_CONTROL_NF) == 0)
707*9999SWang.Lin@Sun.COM 			break;
708*9999SWang.Lin@Sun.COM 		drv_usecwait(10);
709*9999SWang.Lin@Sun.COM 	}
710*9999SWang.Lin@Sun.COM 
711*9999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
712*9999SWang.Lin@Sun.COM 		if (chainmask & (1 << i)) {
713*9999SWang.Lin@Sun.COM 			val = REG_READ(ah, ar5416_cca_regs[i]);
714*9999SWang.Lin@Sun.COM 			val &= 0xFFFFFE00;
715*9999SWang.Lin@Sun.COM 			val |= (((uint32_t)(-50) << 1) & 0x1ff);
716*9999SWang.Lin@Sun.COM 			REG_WRITE(ah, ar5416_cca_regs[i], val);
717*9999SWang.Lin@Sun.COM 		}
718*9999SWang.Lin@Sun.COM 	}
719*9999SWang.Lin@Sun.COM }
720*9999SWang.Lin@Sun.COM 
721*9999SWang.Lin@Sun.COM int16_t
722*9999SWang.Lin@Sun.COM ath9k_hw_getnf(struct ath_hal *ah, struct ath9k_channel *chan)
723*9999SWang.Lin@Sun.COM {
724*9999SWang.Lin@Sun.COM 	int16_t nf, nfThresh;
725*9999SWang.Lin@Sun.COM 	int16_t nfarray[NUM_NF_READINGS] = { 0 };
726*9999SWang.Lin@Sun.COM 	struct ath9k_nfcal_hist *h;
727*9999SWang.Lin@Sun.COM 	/* LINTED E_FUNC_SET_NOT_USED */
728*9999SWang.Lin@Sun.COM 	uint8_t chainmask;
729*9999SWang.Lin@Sun.COM 
730*9999SWang.Lin@Sun.COM 	if (AR_SREV_9280(ah))
731*9999SWang.Lin@Sun.COM 		chainmask = 0x1B;
732*9999SWang.Lin@Sun.COM 	else
733*9999SWang.Lin@Sun.COM 		chainmask = 0x3F;
734*9999SWang.Lin@Sun.COM 
735*9999SWang.Lin@Sun.COM 	chan->channelFlags &= (~CHANNEL_CW_INT);
736*9999SWang.Lin@Sun.COM 	if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
737*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
738*9999SWang.Lin@Sun.COM 		    "%s: NF did not complete in calibration window\n",
739*9999SWang.Lin@Sun.COM 		    __func__));
740*9999SWang.Lin@Sun.COM 		nf = 0;
741*9999SWang.Lin@Sun.COM 		chan->rawNoiseFloor = nf;
742*9999SWang.Lin@Sun.COM 		return (chan->rawNoiseFloor);
743*9999SWang.Lin@Sun.COM 	} else {
744*9999SWang.Lin@Sun.COM 		ath9k_hw_do_getnf(ah, nfarray);
745*9999SWang.Lin@Sun.COM 		nf = nfarray[0];
746*9999SWang.Lin@Sun.COM 		if (getNoiseFloorThresh(ah, chan, &nfThresh) &&
747*9999SWang.Lin@Sun.COM 		    nf > nfThresh) {
748*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
749*9999SWang.Lin@Sun.COM 			    "%s: noise floor failed detected; "
750*9999SWang.Lin@Sun.COM 			    "detected %d, threshold %d\n", __func__,
751*9999SWang.Lin@Sun.COM 			    nf, nfThresh));
752*9999SWang.Lin@Sun.COM 			chan->channelFlags |= CHANNEL_CW_INT;
753*9999SWang.Lin@Sun.COM 		}
754*9999SWang.Lin@Sun.COM 	}
755*9999SWang.Lin@Sun.COM 
756*9999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
757*9999SWang.Lin@Sun.COM 	h = chan->nfCalHist;
758*9999SWang.Lin@Sun.COM #else
759*9999SWang.Lin@Sun.COM 	h = ah->nfCalHist;
760*9999SWang.Lin@Sun.COM #endif
761*9999SWang.Lin@Sun.COM 
762*9999SWang.Lin@Sun.COM 	ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
763*9999SWang.Lin@Sun.COM 	chan->rawNoiseFloor = h[0].privNF;
764*9999SWang.Lin@Sun.COM 
765*9999SWang.Lin@Sun.COM 	return (chan->rawNoiseFloor);
766*9999SWang.Lin@Sun.COM }
767*9999SWang.Lin@Sun.COM 
768*9999SWang.Lin@Sun.COM void
769*9999SWang.Lin@Sun.COM ath9k_init_nfcal_hist_buffer(struct ath_hal *ah)
770*9999SWang.Lin@Sun.COM {
771*9999SWang.Lin@Sun.COM 	int i, j;
772*9999SWang.Lin@Sun.COM 
773*9999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
774*9999SWang.Lin@Sun.COM 		ah->nfCalHist[i].currIndex = 0;
775*9999SWang.Lin@Sun.COM 		ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE;
776*9999SWang.Lin@Sun.COM 		ah->nfCalHist[i].invalidNFcount =
777*9999SWang.Lin@Sun.COM 		    AR_PHY_CCA_FILTERWINDOW_LENGTH;
778*9999SWang.Lin@Sun.COM 		for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
779*9999SWang.Lin@Sun.COM 			ah->nfCalHist[i].nfCalBuffer[j] =
780*9999SWang.Lin@Sun.COM 			    AR_PHY_CCA_MAX_GOOD_VALUE;
781*9999SWang.Lin@Sun.COM 		}
782*9999SWang.Lin@Sun.COM 	}
783*9999SWang.Lin@Sun.COM }
784*9999SWang.Lin@Sun.COM 
785*9999SWang.Lin@Sun.COM signed short
786*9999SWang.Lin@Sun.COM ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan)
787*9999SWang.Lin@Sun.COM {
788*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan;
789*9999SWang.Lin@Sun.COM 	signed short nf;
790*9999SWang.Lin@Sun.COM 
791*9999SWang.Lin@Sun.COM 	ichan = ath9k_regd_check_channel(ah, chan);
792*9999SWang.Lin@Sun.COM 	if (ichan == NULL) {
793*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
794*9999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
795*9999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
796*9999SWang.Lin@Sun.COM 		return (ATH_DEFAULT_NOISE_FLOOR);
797*9999SWang.Lin@Sun.COM 	}
798*9999SWang.Lin@Sun.COM 	if (ichan->rawNoiseFloor == 0) {
799*9999SWang.Lin@Sun.COM 		enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan);
800*9999SWang.Lin@Sun.COM 		nf = NOISE_FLOOR[mode];
801*9999SWang.Lin@Sun.COM 	} else
802*9999SWang.Lin@Sun.COM 		nf = ichan->rawNoiseFloor;
803*9999SWang.Lin@Sun.COM 
804*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_nf_in_range(ah, nf))
805*9999SWang.Lin@Sun.COM 		nf = ATH_DEFAULT_NOISE_FLOOR;
806*9999SWang.Lin@Sun.COM 
807*9999SWang.Lin@Sun.COM 	return (nf);
808*9999SWang.Lin@Sun.COM }
809*9999SWang.Lin@Sun.COM 
810*9999SWang.Lin@Sun.COM boolean_t
811*9999SWang.Lin@Sun.COM ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
812*9999SWang.Lin@Sun.COM     uint8_t rxchainmask, boolean_t longcal,
813*9999SWang.Lin@Sun.COM     boolean_t *isCalDone)
814*9999SWang.Lin@Sun.COM {
815*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
816*9999SWang.Lin@Sun.COM 	struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
817*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
818*9999SWang.Lin@Sun.COM 
819*9999SWang.Lin@Sun.COM 	*isCalDone = B_TRUE;
820*9999SWang.Lin@Sun.COM 
821*9999SWang.Lin@Sun.COM 	if (ichan == NULL) {
822*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL,
823*9999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
824*9999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
825*9999SWang.Lin@Sun.COM 		return (B_FALSE);
826*9999SWang.Lin@Sun.COM 	}
827*9999SWang.Lin@Sun.COM 
828*9999SWang.Lin@Sun.COM 	if (currCal &&
829*9999SWang.Lin@Sun.COM 	    (currCal->calState == CAL_RUNNING ||
830*9999SWang.Lin@Sun.COM 	    currCal->calState == CAL_WAITING)) {
831*9999SWang.Lin@Sun.COM 		ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal,
832*9999SWang.Lin@Sun.COM 		    isCalDone);
833*9999SWang.Lin@Sun.COM 		if (*isCalDone) {
834*9999SWang.Lin@Sun.COM 			ahp->ah_cal_list_curr = currCal = currCal->calNext;
835*9999SWang.Lin@Sun.COM 
836*9999SWang.Lin@Sun.COM 			if (currCal->calState == CAL_WAITING) {
837*9999SWang.Lin@Sun.COM 				*isCalDone = B_FALSE;
838*9999SWang.Lin@Sun.COM 				ath9k_hw_reset_calibration(ah, currCal);
839*9999SWang.Lin@Sun.COM 			}
840*9999SWang.Lin@Sun.COM 		}
841*9999SWang.Lin@Sun.COM 	}
842*9999SWang.Lin@Sun.COM 
843*9999SWang.Lin@Sun.COM 	if (longcal) {
844*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_getnf(ah, ichan);
845*9999SWang.Lin@Sun.COM 		ath9k_hw_loadnf(ah, ah->ah_curchan);
846*9999SWang.Lin@Sun.COM 		ath9k_hw_start_nfcal(ah);
847*9999SWang.Lin@Sun.COM 
848*9999SWang.Lin@Sun.COM 		if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
849*9999SWang.Lin@Sun.COM 			chan->channelFlags |= CHANNEL_CW_INT;
850*9999SWang.Lin@Sun.COM 			ichan->channelFlags &= ~CHANNEL_CW_INT;
851*9999SWang.Lin@Sun.COM 		}
852*9999SWang.Lin@Sun.COM 	}
853*9999SWang.Lin@Sun.COM 
854*9999SWang.Lin@Sun.COM 	return (B_TRUE);
855*9999SWang.Lin@Sun.COM }
856*9999SWang.Lin@Sun.COM 
857*9999SWang.Lin@Sun.COM /* AR9285 */
858*9999SWang.Lin@Sun.COM static inline void
859*9999SWang.Lin@Sun.COM ath9k_hw_9285_pa_cal(struct ath_hal *ah)
860*9999SWang.Lin@Sun.COM {
861*9999SWang.Lin@Sun.COM 
862*9999SWang.Lin@Sun.COM 	uint32_t regVal;
863*9999SWang.Lin@Sun.COM 	int i, offset, offs_6_1, offs_0;
864*9999SWang.Lin@Sun.COM 	uint32_t ccomp_org, reg_field;
865*9999SWang.Lin@Sun.COM 	uint32_t regList[][2] = {
866*9999SWang.Lin@Sun.COM 	    { 0x786c, 0 },
867*9999SWang.Lin@Sun.COM 	    { 0x7854, 0 },
868*9999SWang.Lin@Sun.COM 	    { 0x7820, 0 },
869*9999SWang.Lin@Sun.COM 	    { 0x7824, 0 },
870*9999SWang.Lin@Sun.COM 	    { 0x7868, 0 },
871*9999SWang.Lin@Sun.COM 	    { 0x783c, 0 },
872*9999SWang.Lin@Sun.COM 	    { 0x7838, 0 },
873*9999SWang.Lin@Sun.COM 	};
874*9999SWang.Lin@Sun.COM 
875*9999SWang.Lin@Sun.COM 	if (AR_SREV_9285_11(ah)) {
876*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
877*9999SWang.Lin@Sun.COM 		drv_usecwait(10);
878*9999SWang.Lin@Sun.COM 	}
879*9999SWang.Lin@Sun.COM 
880*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regList); i++)
881*9999SWang.Lin@Sun.COM 		regList[i][1] = REG_READ(ah, regList[i][0]);
882*9999SWang.Lin@Sun.COM 
883*9999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x7834);
884*9999SWang.Lin@Sun.COM 	regVal &= (~(0x1));
885*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x7834, regVal);
886*9999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x9808);
887*9999SWang.Lin@Sun.COM 	regVal |= (0x1 << 27);
888*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x9808, regVal);
889*9999SWang.Lin@Sun.COM 
890*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
891*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
892*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
893*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
894*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
895*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
896*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
897*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 1);
898*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
899*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
900*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
901*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
902*9999SWang.Lin@Sun.COM 	ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP);
903*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 7);
904*9999SWang.Lin@Sun.COM 
905*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0);
906*9999SWang.Lin@Sun.COM 	drv_usecwait(30);
907*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0);
908*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0);
909*9999SWang.Lin@Sun.COM 
910*9999SWang.Lin@Sun.COM 	for (i = 6; i > 0; i--) {
911*9999SWang.Lin@Sun.COM 		regVal = REG_READ(ah, 0x7834);
912*9999SWang.Lin@Sun.COM 		regVal |= (1 << (19 + i));
913*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, 0x7834, regVal);
914*9999SWang.Lin@Sun.COM 		drv_usecwait(1);
915*9999SWang.Lin@Sun.COM 		regVal = REG_READ(ah, 0x7834);
916*9999SWang.Lin@Sun.COM 		regVal &= (~(0x1 << (19 + i)));
917*9999SWang.Lin@Sun.COM 		reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9);
918*9999SWang.Lin@Sun.COM 		regVal |= (reg_field << (19 + i));
919*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, 0x7834, regVal);
920*9999SWang.Lin@Sun.COM 	}
921*9999SWang.Lin@Sun.COM 
922*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1);
923*9999SWang.Lin@Sun.COM 	drv_usecwait(1);
924*9999SWang.Lin@Sun.COM 	reg_field = MS(REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9);
925*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field);
926*9999SWang.Lin@Sun.COM 	offs_6_1 = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS);
927*9999SWang.Lin@Sun.COM 	offs_0   = MS(REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP);
928*9999SWang.Lin@Sun.COM 
929*9999SWang.Lin@Sun.COM 	offset = (offs_6_1<<1) | offs_0;
930*9999SWang.Lin@Sun.COM 	offset = offset - 0;
931*9999SWang.Lin@Sun.COM 	offs_6_1 = offset>>1;
932*9999SWang.Lin@Sun.COM 	offs_0 = offset & 1;
933*9999SWang.Lin@Sun.COM 
934*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1);
935*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0);
936*9999SWang.Lin@Sun.COM 
937*9999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x7834);
938*9999SWang.Lin@Sun.COM 	regVal |= 0x1;
939*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x7834, regVal);
940*9999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x9808);
941*9999SWang.Lin@Sun.COM 	regVal &= (~(0x1 << 27));
942*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x9808, regVal);
943*9999SWang.Lin@Sun.COM 
944*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regList); i++)
945*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, regList[i][0], regList[i][1]);
946*9999SWang.Lin@Sun.COM 
947*9999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org);
948*9999SWang.Lin@Sun.COM 
949*9999SWang.Lin@Sun.COM 	if (AR_SREV_9285_11(ah))
950*9999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT);
951*9999SWang.Lin@Sun.COM 
952*9999SWang.Lin@Sun.COM }
953*9999SWang.Lin@Sun.COM 
954*9999SWang.Lin@Sun.COM boolean_t
955*9999SWang.Lin@Sun.COM ath9k_hw_init_cal(struct ath_hal *ah,
956*9999SWang.Lin@Sun.COM     struct ath9k_channel *chan)
957*9999SWang.Lin@Sun.COM {
958*9999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
959*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
960*9999SWang.Lin@Sun.COM 
961*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
962*9999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_AGC_CONTROL) |
963*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_CAL);
964*9999SWang.Lin@Sun.COM 
965*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
966*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
967*9999SWang.Lin@Sun.COM 		    "%s: offset calibration failed to complete in 1ms; "
968*9999SWang.Lin@Sun.COM 		    "noisy environment?\n", __func__));
969*9999SWang.Lin@Sun.COM 		return (B_FALSE);
970*9999SWang.Lin@Sun.COM 	}
971*9999SWang.Lin@Sun.COM 
972*9999SWang.Lin@Sun.COM 	if (AR_SREV_9285(ah) && AR_SREV_9285_11_OR_LATER(ah))
973*9999SWang.Lin@Sun.COM 			ath9k_hw_9285_pa_cal(ah);
974*9999SWang.Lin@Sun.COM 
975*9999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
976*9999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_AGC_CONTROL) |
977*9999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NF);
978*9999SWang.Lin@Sun.COM 
979*9999SWang.Lin@Sun.COM 	ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL;
980*9999SWang.Lin@Sun.COM 
981*9999SWang.Lin@Sun.COM 	if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) {
982*9999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) {
983*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
984*9999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_adcGainCalData);
985*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
986*9999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_adcGainCalData);
987*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
988*9999SWang.Lin@Sun.COM 			    "%s: enabling ADC Gain Calibration.\n",
989*9999SWang.Lin@Sun.COM 			    __func__));
990*9999SWang.Lin@Sun.COM 		}
991*9999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) {
992*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
993*9999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_adcDcCalData);
994*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
995*9999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_adcDcCalData);
996*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
997*9999SWang.Lin@Sun.COM 			    "%s: enabling ADC DC Calibration.\n",
998*9999SWang.Lin@Sun.COM 			    __func__));
999*9999SWang.Lin@Sun.COM 		}
1000*9999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) {
1001*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
1002*9999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_iqCalData);
1003*9999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
1004*9999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_iqCalData);
1005*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
1006*9999SWang.Lin@Sun.COM 			    "%s: enabling IQ Calibration.\n",
1007*9999SWang.Lin@Sun.COM 			    __func__));
1008*9999SWang.Lin@Sun.COM 		}
1009*9999SWang.Lin@Sun.COM 
1010*9999SWang.Lin@Sun.COM 		ahp->ah_cal_list_curr = ahp->ah_cal_list;
1011*9999SWang.Lin@Sun.COM 
1012*9999SWang.Lin@Sun.COM 		if (ahp->ah_cal_list_curr)
1013*9999SWang.Lin@Sun.COM 			ath9k_hw_reset_calibration(ah, ahp->ah_cal_list_curr);
1014*9999SWang.Lin@Sun.COM 	}
1015*9999SWang.Lin@Sun.COM 
1016*9999SWang.Lin@Sun.COM 	ichan->CalValid = 0;
1017*9999SWang.Lin@Sun.COM 
1018*9999SWang.Lin@Sun.COM 	return (B_TRUE);
1019*9999SWang.Lin@Sun.COM }
1020*9999SWang.Lin@Sun.COM 
1021*9999SWang.Lin@Sun.COM const struct hal_percal_data iq_cal_multi_sample = {
1022*9999SWang.Lin@Sun.COM 	IQ_MISMATCH_CAL,
1023*9999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
1024*9999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
1025*9999SWang.Lin@Sun.COM 	ath9k_hw_iqcal_collect,
1026*9999SWang.Lin@Sun.COM 	ath9k_hw_iqcalibrate
1027*9999SWang.Lin@Sun.COM };
1028*9999SWang.Lin@Sun.COM const struct hal_percal_data iq_cal_single_sample = {
1029*9999SWang.Lin@Sun.COM 	IQ_MISMATCH_CAL,
1030*9999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
1031*9999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
1032*9999SWang.Lin@Sun.COM 	ath9k_hw_iqcal_collect,
1033*9999SWang.Lin@Sun.COM 	ath9k_hw_iqcalibrate
1034*9999SWang.Lin@Sun.COM };
1035*9999SWang.Lin@Sun.COM const struct hal_percal_data adc_gain_cal_multi_sample = {
1036*9999SWang.Lin@Sun.COM 	ADC_GAIN_CAL,
1037*9999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
1038*9999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
1039*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_collect,
1040*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_calibrate
1041*9999SWang.Lin@Sun.COM };
1042*9999SWang.Lin@Sun.COM const struct hal_percal_data adc_gain_cal_single_sample = {
1043*9999SWang.Lin@Sun.COM 	ADC_GAIN_CAL,
1044*9999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
1045*9999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
1046*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_collect,
1047*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_calibrate
1048*9999SWang.Lin@Sun.COM };
1049*9999SWang.Lin@Sun.COM const struct hal_percal_data adc_dc_cal_multi_sample = {
1050*9999SWang.Lin@Sun.COM 	ADC_DC_CAL,
1051*9999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
1052*9999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
1053*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
1054*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
1055*9999SWang.Lin@Sun.COM };
1056*9999SWang.Lin@Sun.COM const struct hal_percal_data adc_dc_cal_single_sample = {
1057*9999SWang.Lin@Sun.COM 	ADC_DC_CAL,
1058*9999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
1059*9999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
1060*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
1061*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
1062*9999SWang.Lin@Sun.COM };
1063*9999SWang.Lin@Sun.COM const struct hal_percal_data adc_init_dc_cal = {
1064*9999SWang.Lin@Sun.COM 	ADC_DC_INIT_CAL,
1065*9999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
1066*9999SWang.Lin@Sun.COM 	INIT_LOG_COUNT,
1067*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
1068*9999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
1069*9999SWang.Lin@Sun.COM };
1070