xref: /onnv-gate/usr/src/uts/common/io/arn/arn_calib.c (revision 11729:8922e660c576)
19999SWang.Lin@Sun.COM /*
2*11729SWang.Lin@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
39999SWang.Lin@Sun.COM  * Use is subject to license terms.
49999SWang.Lin@Sun.COM  */
59999SWang.Lin@Sun.COM 
69999SWang.Lin@Sun.COM /*
79999SWang.Lin@Sun.COM  * Copyright (c) 2008 Atheros Communications Inc.
89999SWang.Lin@Sun.COM  *
99999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
109999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
119999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
129999SWang.Lin@Sun.COM  *
139999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
149999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
159999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
169999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
179999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
189999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
199999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
209999SWang.Lin@Sun.COM  */
219999SWang.Lin@Sun.COM 
229999SWang.Lin@Sun.COM #include "arn_core.h"
239999SWang.Lin@Sun.COM #include "arn_hw.h"
249999SWang.Lin@Sun.COM #include "arn_reg.h"
259999SWang.Lin@Sun.COM #include "arn_phy.h"
269999SWang.Lin@Sun.COM 
279999SWang.Lin@Sun.COM static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 };
289999SWang.Lin@Sun.COM 
299999SWang.Lin@Sun.COM /* We can tune this as we go by monitoring really low values */
309999SWang.Lin@Sun.COM #define	ATH9K_NF_TOO_LOW	-60
319999SWang.Lin@Sun.COM 
329999SWang.Lin@Sun.COM /*
339999SWang.Lin@Sun.COM  * AR5416 may return very high value (like -31 dBm), in those cases the nf
349999SWang.Lin@Sun.COM  * is incorrect and we should use the static NF value. Later we can try to
359999SWang.Lin@Sun.COM  * find out why they are reporting these values
369999SWang.Lin@Sun.COM  */
379999SWang.Lin@Sun.COM 
389999SWang.Lin@Sun.COM /* ARGSUSED */
399999SWang.Lin@Sun.COM static boolean_t
ath9k_hw_nf_in_range(struct ath_hal * ah,signed short nf)409999SWang.Lin@Sun.COM ath9k_hw_nf_in_range(struct ath_hal *ah, signed short nf)
419999SWang.Lin@Sun.COM {
429999SWang.Lin@Sun.COM 	if (nf > ATH9K_NF_TOO_LOW) {
439999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
449999SWang.Lin@Sun.COM 		    "%s: noise floor value detected (%d) is "
459999SWang.Lin@Sun.COM 		    "lower than what we think is a "
469999SWang.Lin@Sun.COM 		    "reasonable value (%d)\n",
479999SWang.Lin@Sun.COM 		    __func__, nf, ATH9K_NF_TOO_LOW));
489999SWang.Lin@Sun.COM 
499999SWang.Lin@Sun.COM 		return (B_FALSE);
509999SWang.Lin@Sun.COM 	}
519999SWang.Lin@Sun.COM 	return (B_TRUE);
529999SWang.Lin@Sun.COM }
539999SWang.Lin@Sun.COM 
549999SWang.Lin@Sun.COM static int16_t
ath9k_hw_get_nf_hist_mid(int16_t * nfCalBuffer)559999SWang.Lin@Sun.COM ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
569999SWang.Lin@Sun.COM {
579999SWang.Lin@Sun.COM 	int16_t nfval;
589999SWang.Lin@Sun.COM 	int16_t sort[ATH9K_NF_CAL_HIST_MAX];
599999SWang.Lin@Sun.COM 	int i, j;
609999SWang.Lin@Sun.COM 
619999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
629999SWang.Lin@Sun.COM 		sort[i] = nfCalBuffer[i];
639999SWang.Lin@Sun.COM 
649999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
659999SWang.Lin@Sun.COM 		for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
669999SWang.Lin@Sun.COM 			if (sort[j] > sort[j - 1]) {
679999SWang.Lin@Sun.COM 				nfval = sort[j];
689999SWang.Lin@Sun.COM 				sort[j] = sort[j - 1];
699999SWang.Lin@Sun.COM 				sort[j - 1] = nfval;
709999SWang.Lin@Sun.COM 			}
719999SWang.Lin@Sun.COM 		}
729999SWang.Lin@Sun.COM 	}
739999SWang.Lin@Sun.COM 	nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
749999SWang.Lin@Sun.COM 
759999SWang.Lin@Sun.COM 	return (nfval);
769999SWang.Lin@Sun.COM }
779999SWang.Lin@Sun.COM 
789999SWang.Lin@Sun.COM static void
ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist * h,int16_t * nfarray)799999SWang.Lin@Sun.COM ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
809999SWang.Lin@Sun.COM     int16_t *nfarray)
819999SWang.Lin@Sun.COM {
829999SWang.Lin@Sun.COM 	int i;
839999SWang.Lin@Sun.COM 
849999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
859999SWang.Lin@Sun.COM 		h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
869999SWang.Lin@Sun.COM 
879999SWang.Lin@Sun.COM 		if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
889999SWang.Lin@Sun.COM 			h[i].currIndex = 0;
899999SWang.Lin@Sun.COM 
909999SWang.Lin@Sun.COM 		if (h[i].invalidNFcount > 0) {
919999SWang.Lin@Sun.COM 			if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE ||
929999SWang.Lin@Sun.COM 			    nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
939999SWang.Lin@Sun.COM 				h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
949999SWang.Lin@Sun.COM 			} else {
959999SWang.Lin@Sun.COM 				h[i].invalidNFcount--;
969999SWang.Lin@Sun.COM 				h[i].privNF = nfarray[i];
979999SWang.Lin@Sun.COM 			}
989999SWang.Lin@Sun.COM 		} else {
999999SWang.Lin@Sun.COM 			h[i].privNF =
1009999SWang.Lin@Sun.COM 			    ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
1019999SWang.Lin@Sun.COM 		}
1029999SWang.Lin@Sun.COM 	}
1039999SWang.Lin@Sun.COM }
1049999SWang.Lin@Sun.COM 
1059999SWang.Lin@Sun.COM static void
ath9k_hw_do_getnf(struct ath_hal * ah,int16_t nfarray[NUM_NF_READINGS])1069999SWang.Lin@Sun.COM ath9k_hw_do_getnf(struct ath_hal *ah,
1079999SWang.Lin@Sun.COM     int16_t nfarray[NUM_NF_READINGS])
1089999SWang.Lin@Sun.COM {
1099999SWang.Lin@Sun.COM 	int16_t nf;
1109999SWang.Lin@Sun.COM 
1119999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
1129999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
1139999SWang.Lin@Sun.COM 	else
1149999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
1159999SWang.Lin@Sun.COM 
1169999SWang.Lin@Sun.COM 	if (nf & 0x100)
1179999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
1189999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
1199999SWang.Lin@Sun.COM 	    "NF calibrated [ctl] [chain 0] is %d\n", nf));
1209999SWang.Lin@Sun.COM 	nfarray[0] = nf;
1219999SWang.Lin@Sun.COM 
1229999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
1239999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
1249999SWang.Lin@Sun.COM 		    AR9280_PHY_CH1_MINCCA_PWR);
1259999SWang.Lin@Sun.COM 	else
1269999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
1279999SWang.Lin@Sun.COM 		    AR_PHY_CH1_MINCCA_PWR);
1289999SWang.Lin@Sun.COM 
1299999SWang.Lin@Sun.COM 	if (nf & 0x100)
1309999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
1319999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
1329999SWang.Lin@Sun.COM 	    "NF calibrated [ctl] [chain 1] is %d\n", nf));
1339999SWang.Lin@Sun.COM 	nfarray[1] = nf;
1349999SWang.Lin@Sun.COM 
1359999SWang.Lin@Sun.COM 	if (!AR_SREV_9280(ah)) {
1369999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH2_CCA),
1379999SWang.Lin@Sun.COM 		    AR_PHY_CH2_MINCCA_PWR);
1389999SWang.Lin@Sun.COM 		if (nf & 0x100)
1399999SWang.Lin@Sun.COM 			nf = 0 - ((nf ^ 0x1ff) + 1);
1409999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
1419999SWang.Lin@Sun.COM 		    "NF calibrated [ctl] [chain 2] is %d\n", nf));
1429999SWang.Lin@Sun.COM 		nfarray[2] = nf;
1439999SWang.Lin@Sun.COM 	}
1449999SWang.Lin@Sun.COM 
1459999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
1469999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
1479999SWang.Lin@Sun.COM 		    AR9280_PHY_EXT_MINCCA_PWR);
1489999SWang.Lin@Sun.COM 	else
1499999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
1509999SWang.Lin@Sun.COM 		    AR_PHY_EXT_MINCCA_PWR);
1519999SWang.Lin@Sun.COM 
1529999SWang.Lin@Sun.COM 	if (nf & 0x100)
1539999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
1549999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
1559999SWang.Lin@Sun.COM 	    "NF calibrated [ext] [chain 0] is %d\n", nf));
1569999SWang.Lin@Sun.COM 	nfarray[3] = nf;
1579999SWang.Lin@Sun.COM 
1589999SWang.Lin@Sun.COM 	if (AR_SREV_9280_10_OR_LATER(ah))
1599999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
1609999SWang.Lin@Sun.COM 		    AR9280_PHY_CH1_EXT_MINCCA_PWR);
1619999SWang.Lin@Sun.COM 	else
1629999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
1639999SWang.Lin@Sun.COM 		    AR_PHY_CH1_EXT_MINCCA_PWR);
1649999SWang.Lin@Sun.COM 
1659999SWang.Lin@Sun.COM 	if (nf & 0x100)
1669999SWang.Lin@Sun.COM 		nf = 0 - ((nf ^ 0x1ff) + 1);
1679999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
1689999SWang.Lin@Sun.COM 	    "NF calibrated [ext] [chain 1] is %d\n", nf));
1699999SWang.Lin@Sun.COM 	nfarray[4] = nf;
1709999SWang.Lin@Sun.COM 
1719999SWang.Lin@Sun.COM 	if (!AR_SREV_9280(ah)) {
1729999SWang.Lin@Sun.COM 		nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA),
1739999SWang.Lin@Sun.COM 		    AR_PHY_CH2_EXT_MINCCA_PWR);
1749999SWang.Lin@Sun.COM 		if (nf & 0x100)
1759999SWang.Lin@Sun.COM 			nf = 0 - ((nf ^ 0x1ff) + 1);
1769999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
1779999SWang.Lin@Sun.COM 		    "NF calibrated [ext] [chain 2] is %d\n", nf));
1789999SWang.Lin@Sun.COM 		nfarray[5] = nf;
1799999SWang.Lin@Sun.COM 	}
1809999SWang.Lin@Sun.COM }
1819999SWang.Lin@Sun.COM 
1829999SWang.Lin@Sun.COM static boolean_t
getNoiseFloorThresh(struct ath_hal * ah,const struct ath9k_channel * chan,int16_t * nft)1839999SWang.Lin@Sun.COM getNoiseFloorThresh(struct ath_hal *ah,
1849999SWang.Lin@Sun.COM     const struct ath9k_channel *chan,
1859999SWang.Lin@Sun.COM     int16_t *nft)
1869999SWang.Lin@Sun.COM {
1879999SWang.Lin@Sun.COM 	switch (chan->chanmode) {
1889999SWang.Lin@Sun.COM 	case CHANNEL_A:
1899999SWang.Lin@Sun.COM 	case CHANNEL_A_HT20:
1909999SWang.Lin@Sun.COM 	case CHANNEL_A_HT40PLUS:
1919999SWang.Lin@Sun.COM 	case CHANNEL_A_HT40MINUS:
1929999SWang.Lin@Sun.COM 		*nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_5);
1939999SWang.Lin@Sun.COM 		break;
1949999SWang.Lin@Sun.COM 	case CHANNEL_B:
1959999SWang.Lin@Sun.COM 	case CHANNEL_G:
1969999SWang.Lin@Sun.COM 	case CHANNEL_G_HT20:
1979999SWang.Lin@Sun.COM 	case CHANNEL_G_HT40PLUS:
1989999SWang.Lin@Sun.COM 	case CHANNEL_G_HT40MINUS:
1999999SWang.Lin@Sun.COM 		*nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_2);
2009999SWang.Lin@Sun.COM 		break;
2019999SWang.Lin@Sun.COM 	default:
2029999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL,
2039999SWang.Lin@Sun.COM 		    "%s: invalid channel flags 0x%x\n", __func__,
2049999SWang.Lin@Sun.COM 		    chan->channelFlags));
2059999SWang.Lin@Sun.COM 		return (B_FALSE);
2069999SWang.Lin@Sun.COM 	}
2079999SWang.Lin@Sun.COM 
2089999SWang.Lin@Sun.COM 	return (B_TRUE);
2099999SWang.Lin@Sun.COM }
2109999SWang.Lin@Sun.COM 
2119999SWang.Lin@Sun.COM static void
ath9k_hw_setup_calibration(struct ath_hal * ah,struct hal_cal_list * currCal)2129999SWang.Lin@Sun.COM ath9k_hw_setup_calibration(struct ath_hal *ah,
2139999SWang.Lin@Sun.COM     struct hal_cal_list *currCal)
2149999SWang.Lin@Sun.COM {
2159999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0),
2169999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
2179999SWang.Lin@Sun.COM 	    currCal->calData->calCountMax);
2189999SWang.Lin@Sun.COM 
2199999SWang.Lin@Sun.COM 	switch (currCal->calData->calType) {
2209999SWang.Lin@Sun.COM 	case IQ_MISMATCH_CAL:
2219999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
2229999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
2239999SWang.Lin@Sun.COM 		    "%s: starting IQ Mismatch Calibration\n",
2249999SWang.Lin@Sun.COM 		    __func__));
2259999SWang.Lin@Sun.COM 		break;
2269999SWang.Lin@Sun.COM 	case ADC_GAIN_CAL:
2279999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
2289999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
2299999SWang.Lin@Sun.COM 		    "%s: starting ADC Gain Calibration\n", __func__));
2309999SWang.Lin@Sun.COM 		break;
2319999SWang.Lin@Sun.COM 	case ADC_DC_CAL:
2329999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
2339999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
2349999SWang.Lin@Sun.COM 		    "%s: starting ADC DC Calibration\n", __func__));
2359999SWang.Lin@Sun.COM 		break;
2369999SWang.Lin@Sun.COM 	case ADC_DC_INIT_CAL:
2379999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
2389999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
2399999SWang.Lin@Sun.COM 		    "%s: starting Init ADC DC Calibration\n",
2409999SWang.Lin@Sun.COM 		    __func__));
2419999SWang.Lin@Sun.COM 		break;
2429999SWang.Lin@Sun.COM 	}
2439999SWang.Lin@Sun.COM 
2449999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
2459999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_DO_CAL);
2469999SWang.Lin@Sun.COM }
2479999SWang.Lin@Sun.COM 
2489999SWang.Lin@Sun.COM static void
ath9k_hw_reset_calibration(struct ath_hal * ah,struct hal_cal_list * currCal)2499999SWang.Lin@Sun.COM ath9k_hw_reset_calibration(struct ath_hal *ah,
2509999SWang.Lin@Sun.COM     struct hal_cal_list *currCal)
2519999SWang.Lin@Sun.COM {
2529999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
2539999SWang.Lin@Sun.COM 	int i;
2549999SWang.Lin@Sun.COM 
2559999SWang.Lin@Sun.COM 	ath9k_hw_setup_calibration(ah, currCal);
2569999SWang.Lin@Sun.COM 
2579999SWang.Lin@Sun.COM 	currCal->calState = CAL_RUNNING;
2589999SWang.Lin@Sun.COM 
2599999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
2609999SWang.Lin@Sun.COM 		ahp->ah_Meas0.sign[i] = 0;
2619999SWang.Lin@Sun.COM 		ahp->ah_Meas1.sign[i] = 0;
2629999SWang.Lin@Sun.COM 		ahp->ah_Meas2.sign[i] = 0;
2639999SWang.Lin@Sun.COM 		ahp->ah_Meas3.sign[i] = 0;
2649999SWang.Lin@Sun.COM 	}
2659999SWang.Lin@Sun.COM 
2669999SWang.Lin@Sun.COM 	ahp->ah_CalSamples = 0;
2679999SWang.Lin@Sun.COM }
2689999SWang.Lin@Sun.COM 
2699999SWang.Lin@Sun.COM static void
ath9k_hw_per_calibration(struct ath_hal * ah,struct ath9k_channel * ichan,uint8_t rxchainmask,struct hal_cal_list * currCal,boolean_t * isCalDone)2709999SWang.Lin@Sun.COM ath9k_hw_per_calibration(struct ath_hal *ah,
2719999SWang.Lin@Sun.COM     struct ath9k_channel *ichan,
2729999SWang.Lin@Sun.COM     uint8_t rxchainmask,
2739999SWang.Lin@Sun.COM     struct hal_cal_list *currCal,
2749999SWang.Lin@Sun.COM     boolean_t *isCalDone)
2759999SWang.Lin@Sun.COM {
2769999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
2779999SWang.Lin@Sun.COM 
2789999SWang.Lin@Sun.COM 	*isCalDone = B_FALSE;
2799999SWang.Lin@Sun.COM 
2809999SWang.Lin@Sun.COM 	if (currCal->calState == CAL_RUNNING) {
2819999SWang.Lin@Sun.COM 		if (!(REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
2829999SWang.Lin@Sun.COM 		    AR_PHY_TIMING_CTRL4_DO_CAL)) {
2839999SWang.Lin@Sun.COM 
2849999SWang.Lin@Sun.COM 			currCal->calData->calCollect(ah);
2859999SWang.Lin@Sun.COM 			ahp->ah_CalSamples++;
2869999SWang.Lin@Sun.COM 
2879999SWang.Lin@Sun.COM 			if (ahp->ah_CalSamples >=
2889999SWang.Lin@Sun.COM 			    currCal->calData->calNumSamples) {
2899999SWang.Lin@Sun.COM 				int i, numChains = 0;
2909999SWang.Lin@Sun.COM 				for (i = 0; i < AR5416_MAX_CHAINS; i++) {
2919999SWang.Lin@Sun.COM 					if (rxchainmask & (1 << i))
2929999SWang.Lin@Sun.COM 						numChains++;
2939999SWang.Lin@Sun.COM 				}
2949999SWang.Lin@Sun.COM 
2959999SWang.Lin@Sun.COM 				currCal->calData->calPostProc(ah, numChains);
2969999SWang.Lin@Sun.COM 				ichan->CalValid |= currCal->calData->calType;
2979999SWang.Lin@Sun.COM 				currCal->calState = CAL_DONE;
2989999SWang.Lin@Sun.COM 				*isCalDone = B_TRUE;
2999999SWang.Lin@Sun.COM 			} else {
3009999SWang.Lin@Sun.COM 				ath9k_hw_setup_calibration(ah, currCal);
3019999SWang.Lin@Sun.COM 			}
3029999SWang.Lin@Sun.COM 		}
3039999SWang.Lin@Sun.COM 	} else if (!(ichan->CalValid & currCal->calData->calType)) {
3049999SWang.Lin@Sun.COM 		ath9k_hw_reset_calibration(ah, currCal);
3059999SWang.Lin@Sun.COM 	}
3069999SWang.Lin@Sun.COM }
3079999SWang.Lin@Sun.COM 
3089999SWang.Lin@Sun.COM static boolean_t
ath9k_hw_iscal_supported(struct ath_hal * ah,struct ath9k_channel * chan,enum hal_cal_types calType)3099999SWang.Lin@Sun.COM ath9k_hw_iscal_supported(struct ath_hal *ah,
3109999SWang.Lin@Sun.COM     struct ath9k_channel *chan,
3119999SWang.Lin@Sun.COM     enum hal_cal_types calType)
3129999SWang.Lin@Sun.COM {
3139999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
3149999SWang.Lin@Sun.COM 	boolean_t retval = B_FALSE;
3159999SWang.Lin@Sun.COM 
3169999SWang.Lin@Sun.COM 	switch (calType & ahp->ah_suppCals) {
3179999SWang.Lin@Sun.COM 	case IQ_MISMATCH_CAL:
3189999SWang.Lin@Sun.COM 		if (!IS_CHAN_B(chan))
3199999SWang.Lin@Sun.COM 			retval = B_TRUE;
3209999SWang.Lin@Sun.COM 		break;
3219999SWang.Lin@Sun.COM 	case ADC_GAIN_CAL:
3229999SWang.Lin@Sun.COM 	case ADC_DC_CAL:
3239999SWang.Lin@Sun.COM 		if (!IS_CHAN_B(chan) &&
3249999SWang.Lin@Sun.COM 		    !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan)))
3259999SWang.Lin@Sun.COM 			retval = B_TRUE;
3269999SWang.Lin@Sun.COM 		break;
3279999SWang.Lin@Sun.COM 	}
3289999SWang.Lin@Sun.COM 
3299999SWang.Lin@Sun.COM 	return (retval);
3309999SWang.Lin@Sun.COM }
3319999SWang.Lin@Sun.COM 
3329999SWang.Lin@Sun.COM static void
ath9k_hw_iqcal_collect(struct ath_hal * ah)3339999SWang.Lin@Sun.COM ath9k_hw_iqcal_collect(struct ath_hal *ah)
3349999SWang.Lin@Sun.COM {
3359999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
3369999SWang.Lin@Sun.COM 	int i;
3379999SWang.Lin@Sun.COM 
3389999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
3399999SWang.Lin@Sun.COM 		ahp->ah_totalPowerMeasI[i] +=
3409999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
3419999SWang.Lin@Sun.COM 		ahp->ah_totalPowerMeasQ[i] +=
3429999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
3439999SWang.Lin@Sun.COM 		ahp->ah_totalIqCorrMeas[i] +=
3449999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
3459999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
3469999SWang.Lin@Sun.COM 		    "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
3479999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i],
3489999SWang.Lin@Sun.COM 		    ahp->ah_totalPowerMeasQ[i],
3499999SWang.Lin@Sun.COM 		    ahp->ah_totalIqCorrMeas[i]));
3509999SWang.Lin@Sun.COM 	}
3519999SWang.Lin@Sun.COM }
3529999SWang.Lin@Sun.COM 
3539999SWang.Lin@Sun.COM static void
ath9k_hw_adc_gaincal_collect(struct ath_hal * ah)3549999SWang.Lin@Sun.COM ath9k_hw_adc_gaincal_collect(struct ath_hal *ah)
3559999SWang.Lin@Sun.COM {
3569999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
3579999SWang.Lin@Sun.COM 	int i;
3589999SWang.Lin@Sun.COM 
3599999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
3609999SWang.Lin@Sun.COM 		ahp->ah_totalAdcIOddPhase[i] +=
3619999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
3629999SWang.Lin@Sun.COM 		ahp->ah_totalAdcIEvenPhase[i] +=
3639999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
3649999SWang.Lin@Sun.COM 		ahp->ah_totalAdcQOddPhase[i] +=
3659999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
3669999SWang.Lin@Sun.COM 		ahp->ah_totalAdcQEvenPhase[i] +=
3679999SWang.Lin@Sun.COM 		    REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
3689999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
3699999SWang.Lin@Sun.COM 		    "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
3709999SWang.Lin@Sun.COM 		    "oddq=0x%08x; evenq=0x%08x;\n",
3719999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i,
3729999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcIOddPhase[i],
3739999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcIEvenPhase[i],
3749999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcQOddPhase[i],
3759999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcQEvenPhase[i]));
3769999SWang.Lin@Sun.COM 	}
3779999SWang.Lin@Sun.COM }
3789999SWang.Lin@Sun.COM 
3799999SWang.Lin@Sun.COM static void
ath9k_hw_adc_dccal_collect(struct ath_hal * ah)3809999SWang.Lin@Sun.COM ath9k_hw_adc_dccal_collect(struct ath_hal *ah)
3819999SWang.Lin@Sun.COM {
3829999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
3839999SWang.Lin@Sun.COM 	int i;
3849999SWang.Lin@Sun.COM 
3859999SWang.Lin@Sun.COM 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
3869999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetIOddPhase[i] +=
3879999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
3889999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetIEvenPhase[i] +=
3899999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
3909999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetQOddPhase[i] +=
3919999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
3929999SWang.Lin@Sun.COM 		ahp->ah_totalAdcDcOffsetQEvenPhase[i] +=
3939999SWang.Lin@Sun.COM 		    (int32_t)REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
3949999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
3959999SWang.Lin@Sun.COM 		    "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
3969999SWang.Lin@Sun.COM 		    "oddq=0x%08x; evenq=0x%08x;\n",
3979999SWang.Lin@Sun.COM 		    ahp->ah_CalSamples, i,
3989999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetIOddPhase[i],
3999999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetIEvenPhase[i],
4009999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetQOddPhase[i],
4019999SWang.Lin@Sun.COM 		    ahp->ah_totalAdcDcOffsetQEvenPhase[i]));
4029999SWang.Lin@Sun.COM 	}
4039999SWang.Lin@Sun.COM }
4049999SWang.Lin@Sun.COM 
4059999SWang.Lin@Sun.COM static void
ath9k_hw_iqcalibrate(struct ath_hal * ah,uint8_t numChains)4069999SWang.Lin@Sun.COM ath9k_hw_iqcalibrate(struct ath_hal *ah, uint8_t numChains)
4079999SWang.Lin@Sun.COM {
4089999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
4099999SWang.Lin@Sun.COM 	uint32_t powerMeasQ, powerMeasI, iqCorrMeas;
4109999SWang.Lin@Sun.COM 	uint32_t qCoffDenom, iCoffDenom;
4119999SWang.Lin@Sun.COM 	int32_t qCoff, iCoff;
4129999SWang.Lin@Sun.COM 	int iqCorrNeg, i;
4139999SWang.Lin@Sun.COM 
4149999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
4159999SWang.Lin@Sun.COM 		powerMeasI = ahp->ah_totalPowerMeasI[i];
4169999SWang.Lin@Sun.COM 		powerMeasQ = ahp->ah_totalPowerMeasQ[i];
4179999SWang.Lin@Sun.COM 		iqCorrMeas = ahp->ah_totalIqCorrMeas[i];
4189999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
4199999SWang.Lin@Sun.COM 		    "Starting IQ Cal and Correction for Chain %d\n",
4209999SWang.Lin@Sun.COM 		    i));
4219999SWang.Lin@Sun.COM 
4229999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
4239999SWang.Lin@Sun.COM 		    "Orignal: Chn %diq_corr_meas = 0x%08x\n",
4249999SWang.Lin@Sun.COM 		    i, ahp->ah_totalIqCorrMeas[i]));
4259999SWang.Lin@Sun.COM 
4269999SWang.Lin@Sun.COM 		iqCorrNeg = 0;
4279999SWang.Lin@Sun.COM 
4289999SWang.Lin@Sun.COM 		if (iqCorrMeas > 0x80000000) {
4299999SWang.Lin@Sun.COM 			iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
4309999SWang.Lin@Sun.COM 			iqCorrNeg = 1;
4319999SWang.Lin@Sun.COM 		}
4329999SWang.Lin@Sun.COM 
4339999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
4349999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI));
4359999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
4369999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ));
4379999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
4389999SWang.Lin@Sun.COM 		    iqCorrNeg));
4399999SWang.Lin@Sun.COM 
4409999SWang.Lin@Sun.COM 		iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
4419999SWang.Lin@Sun.COM 		qCoffDenom = powerMeasQ / 64;
4429999SWang.Lin@Sun.COM 
4439999SWang.Lin@Sun.COM 		if (powerMeasQ != 0) {
4449999SWang.Lin@Sun.COM 			iCoff = iqCorrMeas / iCoffDenom;
4459999SWang.Lin@Sun.COM 			qCoff = powerMeasI / qCoffDenom - 64;
4469999SWang.Lin@Sun.COM 
4479999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
4489999SWang.Lin@Sun.COM 			    "Chn %d iCoff = 0x%08x\n", i, iCoff));
4499999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
4509999SWang.Lin@Sun.COM 			    "Chn %d qCoff = 0x%08x\n", i, qCoff));
4519999SWang.Lin@Sun.COM 
4529999SWang.Lin@Sun.COM 			iCoff = iCoff & 0x3f;
4539999SWang.Lin@Sun.COM 
4549999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
4559999SWang.Lin@Sun.COM 			    "New: Chn %d iCoff = 0x%08x\n", i, iCoff));
4569999SWang.Lin@Sun.COM 
4579999SWang.Lin@Sun.COM 			if (iqCorrNeg == 0x0)
4589999SWang.Lin@Sun.COM 				iCoff = 0x40 - iCoff;
4599999SWang.Lin@Sun.COM 
4609999SWang.Lin@Sun.COM 			if (qCoff > 15)
4619999SWang.Lin@Sun.COM 				qCoff = 15;
4629999SWang.Lin@Sun.COM 			else if (qCoff <= -16)
4639999SWang.Lin@Sun.COM 				qCoff = 16;
4649999SWang.Lin@Sun.COM 
4659999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
4669999SWang.Lin@Sun.COM 			    "Chn %d : iCoff = 0x%x  qCoff = 0x%x\n",
4679999SWang.Lin@Sun.COM 			    i, iCoff, qCoff));
4689999SWang.Lin@Sun.COM 
4699999SWang.Lin@Sun.COM 			REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
4709999SWang.Lin@Sun.COM 			    AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
4719999SWang.Lin@Sun.COM 			    iCoff);
4729999SWang.Lin@Sun.COM 			REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
4739999SWang.Lin@Sun.COM 			    AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
4749999SWang.Lin@Sun.COM 			    qCoff);
4759999SWang.Lin@Sun.COM 
4769999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
4779999SWang.Lin@Sun.COM 			    "IQ Cal and Correction done for Chain %d\n",
4789999SWang.Lin@Sun.COM 			    i));
4799999SWang.Lin@Sun.COM 		}
4809999SWang.Lin@Sun.COM 	}
4819999SWang.Lin@Sun.COM 
4829999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
4839999SWang.Lin@Sun.COM 	    AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
4849999SWang.Lin@Sun.COM }
4859999SWang.Lin@Sun.COM 
4869999SWang.Lin@Sun.COM static void
ath9k_hw_adc_gaincal_calibrate(struct ath_hal * ah,uint8_t numChains)4879999SWang.Lin@Sun.COM ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, uint8_t numChains)
4889999SWang.Lin@Sun.COM {
4899999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
4909999SWang.Lin@Sun.COM 	uint32_t iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset,
4919999SWang.Lin@Sun.COM 	    qEvenMeasOffset;
4929999SWang.Lin@Sun.COM 	uint32_t qGainMismatch, iGainMismatch, val, i;
4939999SWang.Lin@Sun.COM 
4949999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
4959999SWang.Lin@Sun.COM 		iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i];
4969999SWang.Lin@Sun.COM 		iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i];
4979999SWang.Lin@Sun.COM 		qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i];
4989999SWang.Lin@Sun.COM 		qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i];
4999999SWang.Lin@Sun.COM 
5009999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5019999SWang.Lin@Sun.COM 		    "Starting ADC Gain Cal for Chain %d\n", i));
5029999SWang.Lin@Sun.COM 
5039999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5049999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
5059999SWang.Lin@Sun.COM 		    iOddMeasOffset));
5069999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5079999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_i = 0x%08x\n", i,
5089999SWang.Lin@Sun.COM 		    iEvenMeasOffset));
5099999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5109999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
5119999SWang.Lin@Sun.COM 		    qOddMeasOffset));
5129999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5139999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_q = 0x%08x\n", i,
5149999SWang.Lin@Sun.COM 		    qEvenMeasOffset));
5159999SWang.Lin@Sun.COM 
5169999SWang.Lin@Sun.COM 		if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) {
5179999SWang.Lin@Sun.COM 			iGainMismatch =
5189999SWang.Lin@Sun.COM 			    ((iEvenMeasOffset * 32) /
5199999SWang.Lin@Sun.COM 			    iOddMeasOffset) & 0x3f;
5209999SWang.Lin@Sun.COM 			qGainMismatch =
5219999SWang.Lin@Sun.COM 			    ((qOddMeasOffset * 32) /
5229999SWang.Lin@Sun.COM 			    qEvenMeasOffset) & 0x3f;
5239999SWang.Lin@Sun.COM 
5249999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
5259999SWang.Lin@Sun.COM 			    "Chn %d gain_mismatch_i = 0x%08x\n", i,
5269999SWang.Lin@Sun.COM 			    iGainMismatch));
5279999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
5289999SWang.Lin@Sun.COM 			    "Chn %d gain_mismatch_q = 0x%08x\n", i,
5299999SWang.Lin@Sun.COM 			    qGainMismatch));
5309999SWang.Lin@Sun.COM 
5319999SWang.Lin@Sun.COM 			val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
5329999SWang.Lin@Sun.COM 			val &= 0xfffff000;
5339999SWang.Lin@Sun.COM 			val |= (qGainMismatch) | (iGainMismatch << 6);
5349999SWang.Lin@Sun.COM 			REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
5359999SWang.Lin@Sun.COM 
5369999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
5379999SWang.Lin@Sun.COM 			    "ADC Gain Cal done for Chain %d\n", i));
5389999SWang.Lin@Sun.COM 		}
5399999SWang.Lin@Sun.COM 	}
5409999SWang.Lin@Sun.COM 
5419999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
5429999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
5439999SWang.Lin@Sun.COM 	    AR_PHY_NEW_ADC_GAIN_CORR_ENABLE);
5449999SWang.Lin@Sun.COM }
5459999SWang.Lin@Sun.COM 
5469999SWang.Lin@Sun.COM static void
ath9k_hw_adc_dccal_calibrate(struct ath_hal * ah,uint8_t numChains)5479999SWang.Lin@Sun.COM ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, uint8_t numChains)
5489999SWang.Lin@Sun.COM {
5499999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
5509999SWang.Lin@Sun.COM 	uint32_t iOddMeasOffset, iEvenMeasOffset, val, i;
5519999SWang.Lin@Sun.COM 	int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch;
5529999SWang.Lin@Sun.COM 	const struct hal_percal_data *calData =
5539999SWang.Lin@Sun.COM 	    ahp->ah_cal_list_curr->calData;
5549999SWang.Lin@Sun.COM 	uint32_t numSamples =
5559999SWang.Lin@Sun.COM 	    (1 << (calData->calCountMax + 5)) * calData->calNumSamples;
5569999SWang.Lin@Sun.COM 
5579999SWang.Lin@Sun.COM 	for (i = 0; i < numChains; i++) {
5589999SWang.Lin@Sun.COM 		iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i];
5599999SWang.Lin@Sun.COM 		iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i];
5609999SWang.Lin@Sun.COM 		qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i];
5619999SWang.Lin@Sun.COM 		qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i];
5629999SWang.Lin@Sun.COM 
5639999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5649999SWang.Lin@Sun.COM 		    "Starting ADC DC Offset Cal for Chain %d\n", i));
5659999SWang.Lin@Sun.COM 
5669999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5679999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_i = %d\n", i,
5689999SWang.Lin@Sun.COM 		    iOddMeasOffset));
5699999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5709999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_i = %d\n", i,
5719999SWang.Lin@Sun.COM 		    iEvenMeasOffset));
5729999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5739999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_odd_q = %d\n", i,
5749999SWang.Lin@Sun.COM 		    qOddMeasOffset));
5759999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5769999SWang.Lin@Sun.COM 		    "Chn %d pwr_meas_even_q = %d\n", i,
5779999SWang.Lin@Sun.COM 		    qEvenMeasOffset));
5789999SWang.Lin@Sun.COM 
5799999SWang.Lin@Sun.COM 		iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) /
5809999SWang.Lin@Sun.COM 		    numSamples) & 0x1ff;
5819999SWang.Lin@Sun.COM 		qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) /
5829999SWang.Lin@Sun.COM 		    numSamples) & 0x1ff;
5839999SWang.Lin@Sun.COM 
5849999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5859999SWang.Lin@Sun.COM 		    "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
5869999SWang.Lin@Sun.COM 		    iDcMismatch));
5879999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5889999SWang.Lin@Sun.COM 		    "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
5899999SWang.Lin@Sun.COM 		    qDcMismatch));
5909999SWang.Lin@Sun.COM 
5919999SWang.Lin@Sun.COM 		val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
5929999SWang.Lin@Sun.COM 		val &= 0xc0000fff;
5939999SWang.Lin@Sun.COM 		val |= (qDcMismatch << 12) | (iDcMismatch << 21);
5949999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
5959999SWang.Lin@Sun.COM 
5969999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
5979999SWang.Lin@Sun.COM 		    "ADC DC Offset Cal done for Chain %d\n", i));
5989999SWang.Lin@Sun.COM 	}
5999999SWang.Lin@Sun.COM 
6009999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
6019999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
6029999SWang.Lin@Sun.COM 	    AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE);
6039999SWang.Lin@Sun.COM }
6049999SWang.Lin@Sun.COM 
6059999SWang.Lin@Sun.COM void
ath9k_hw_reset_calvalid(struct ath_hal * ah,struct ath9k_channel * chan,boolean_t * isCalDone)6069999SWang.Lin@Sun.COM ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
6079999SWang.Lin@Sun.COM     boolean_t *isCalDone)
6089999SWang.Lin@Sun.COM {
6099999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
6109999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan =
6119999SWang.Lin@Sun.COM 	    ath9k_regd_check_channel(ah, chan);
6129999SWang.Lin@Sun.COM 	struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
6139999SWang.Lin@Sun.COM 
6149999SWang.Lin@Sun.COM 	*isCalDone = B_TRUE;
6159999SWang.Lin@Sun.COM 
6169999SWang.Lin@Sun.COM 	if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
6179999SWang.Lin@Sun.COM 		return;
6189999SWang.Lin@Sun.COM 
6199999SWang.Lin@Sun.COM 	if (currCal == NULL)
6209999SWang.Lin@Sun.COM 		return;
6219999SWang.Lin@Sun.COM 
6229999SWang.Lin@Sun.COM 	if (ichan == NULL) {
6239999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
6249999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
6259999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
6269999SWang.Lin@Sun.COM 		return;
6279999SWang.Lin@Sun.COM 	}
6289999SWang.Lin@Sun.COM 
6299999SWang.Lin@Sun.COM 
6309999SWang.Lin@Sun.COM 	if (currCal->calState != CAL_DONE) {
6319999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
6329999SWang.Lin@Sun.COM 		    "%s: Calibration state incorrect, %d\n",
6339999SWang.Lin@Sun.COM 		    __func__, currCal->calState));
6349999SWang.Lin@Sun.COM 		return;
6359999SWang.Lin@Sun.COM 	}
6369999SWang.Lin@Sun.COM 
6379999SWang.Lin@Sun.COM 
6389999SWang.Lin@Sun.COM 	if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType))
6399999SWang.Lin@Sun.COM 		return;
6409999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CALIBRATE,
6419999SWang.Lin@Sun.COM 	    "%s: Resetting Cal %d state for channel %u/0x%x\n",
6429999SWang.Lin@Sun.COM 	    __func__, currCal->calData->calType, chan->channel,
6439999SWang.Lin@Sun.COM 	    chan->channelFlags));
6449999SWang.Lin@Sun.COM 
6459999SWang.Lin@Sun.COM 	ichan->CalValid &= ~currCal->calData->calType;
6469999SWang.Lin@Sun.COM 	currCal->calState = CAL_WAITING;
6479999SWang.Lin@Sun.COM 
6489999SWang.Lin@Sun.COM 	*isCalDone = B_FALSE;
6499999SWang.Lin@Sun.COM }
6509999SWang.Lin@Sun.COM 
6519999SWang.Lin@Sun.COM void
ath9k_hw_start_nfcal(struct ath_hal * ah)6529999SWang.Lin@Sun.COM ath9k_hw_start_nfcal(struct ath_hal *ah)
6539999SWang.Lin@Sun.COM {
6549999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
6559999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_ENABLE_NF);
6569999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
6579999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
6589999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
6599999SWang.Lin@Sun.COM }
6609999SWang.Lin@Sun.COM 
6619999SWang.Lin@Sun.COM /* ARGSUSED */
6629999SWang.Lin@Sun.COM void
ath9k_hw_loadnf(struct ath_hal * ah,struct ath9k_channel * chan)6639999SWang.Lin@Sun.COM ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
6649999SWang.Lin@Sun.COM {
6659999SWang.Lin@Sun.COM 	struct ath9k_nfcal_hist *h;
6669999SWang.Lin@Sun.COM 	int i, j;
6679999SWang.Lin@Sun.COM 	int32_t val;
6689999SWang.Lin@Sun.COM 	const uint32_t ar5416_cca_regs[6] = {
6699999SWang.Lin@Sun.COM 		AR_PHY_CCA,
6709999SWang.Lin@Sun.COM 		AR_PHY_CH1_CCA,
6719999SWang.Lin@Sun.COM 		AR_PHY_CH2_CCA,
6729999SWang.Lin@Sun.COM 		AR_PHY_EXT_CCA,
6739999SWang.Lin@Sun.COM 		AR_PHY_CH1_EXT_CCA,
6749999SWang.Lin@Sun.COM 		AR_PHY_CH2_EXT_CCA
6759999SWang.Lin@Sun.COM 	};
6769999SWang.Lin@Sun.COM 	uint8_t chainmask;
6779999SWang.Lin@Sun.COM 
6789999SWang.Lin@Sun.COM 	if (AR_SREV_9280(ah))
6799999SWang.Lin@Sun.COM 		chainmask = 0x1B;
6809999SWang.Lin@Sun.COM 	else
6819999SWang.Lin@Sun.COM 		chainmask = 0x3F;
6829999SWang.Lin@Sun.COM 
6839999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
6849999SWang.Lin@Sun.COM 	h = chan->nfCalHist;
6859999SWang.Lin@Sun.COM #else
6869999SWang.Lin@Sun.COM 	h = ah->nfCalHist;
6879999SWang.Lin@Sun.COM #endif
6889999SWang.Lin@Sun.COM 
6899999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
6909999SWang.Lin@Sun.COM 		if (chainmask & (1 << i)) {
6919999SWang.Lin@Sun.COM 			val = REG_READ(ah, ar5416_cca_regs[i]);
6929999SWang.Lin@Sun.COM 			val &= 0xFFFFFE00;
6939999SWang.Lin@Sun.COM 			val |= (((uint32_t)(h[i].privNF) << 1) & 0x1ff);
6949999SWang.Lin@Sun.COM 			REG_WRITE(ah, ar5416_cca_regs[i], val);
6959999SWang.Lin@Sun.COM 		}
6969999SWang.Lin@Sun.COM 	}
6979999SWang.Lin@Sun.COM 
6989999SWang.Lin@Sun.COM 	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
6999999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_ENABLE_NF);
7009999SWang.Lin@Sun.COM 	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
7019999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
7029999SWang.Lin@Sun.COM 	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
7039999SWang.Lin@Sun.COM 
7049999SWang.Lin@Sun.COM 	for (j = 0; j < 1000; j++) {
7059999SWang.Lin@Sun.COM 		if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
7069999SWang.Lin@Sun.COM 		    AR_PHY_AGC_CONTROL_NF) == 0)
7079999SWang.Lin@Sun.COM 			break;
7089999SWang.Lin@Sun.COM 		drv_usecwait(10);
7099999SWang.Lin@Sun.COM 	}
7109999SWang.Lin@Sun.COM 
7119999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
7129999SWang.Lin@Sun.COM 		if (chainmask & (1 << i)) {
7139999SWang.Lin@Sun.COM 			val = REG_READ(ah, ar5416_cca_regs[i]);
7149999SWang.Lin@Sun.COM 			val &= 0xFFFFFE00;
7159999SWang.Lin@Sun.COM 			val |= (((uint32_t)(-50) << 1) & 0x1ff);
7169999SWang.Lin@Sun.COM 			REG_WRITE(ah, ar5416_cca_regs[i], val);
7179999SWang.Lin@Sun.COM 		}
7189999SWang.Lin@Sun.COM 	}
7199999SWang.Lin@Sun.COM }
7209999SWang.Lin@Sun.COM 
7219999SWang.Lin@Sun.COM int16_t
ath9k_hw_getnf(struct ath_hal * ah,struct ath9k_channel * chan)7229999SWang.Lin@Sun.COM ath9k_hw_getnf(struct ath_hal *ah, struct ath9k_channel *chan)
7239999SWang.Lin@Sun.COM {
7249999SWang.Lin@Sun.COM 	int16_t nf, nfThresh;
7259999SWang.Lin@Sun.COM 	int16_t nfarray[NUM_NF_READINGS] = { 0 };
7269999SWang.Lin@Sun.COM 	struct ath9k_nfcal_hist *h;
7279999SWang.Lin@Sun.COM 	/* LINTED E_FUNC_SET_NOT_USED */
7289999SWang.Lin@Sun.COM 	uint8_t chainmask;
7299999SWang.Lin@Sun.COM 
7309999SWang.Lin@Sun.COM 	if (AR_SREV_9280(ah))
7319999SWang.Lin@Sun.COM 		chainmask = 0x1B;
7329999SWang.Lin@Sun.COM 	else
7339999SWang.Lin@Sun.COM 		chainmask = 0x3F;
7349999SWang.Lin@Sun.COM 
7359999SWang.Lin@Sun.COM 	chan->channelFlags &= (~CHANNEL_CW_INT);
7369999SWang.Lin@Sun.COM 	if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
7379999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
7389999SWang.Lin@Sun.COM 		    "%s: NF did not complete in calibration window\n",
7399999SWang.Lin@Sun.COM 		    __func__));
7409999SWang.Lin@Sun.COM 		nf = 0;
7419999SWang.Lin@Sun.COM 		chan->rawNoiseFloor = nf;
7429999SWang.Lin@Sun.COM 		return (chan->rawNoiseFloor);
7439999SWang.Lin@Sun.COM 	} else {
7449999SWang.Lin@Sun.COM 		ath9k_hw_do_getnf(ah, nfarray);
7459999SWang.Lin@Sun.COM 		nf = nfarray[0];
7469999SWang.Lin@Sun.COM 		if (getNoiseFloorThresh(ah, chan, &nfThresh) &&
7479999SWang.Lin@Sun.COM 		    nf > nfThresh) {
7489999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
7499999SWang.Lin@Sun.COM 			    "%s: noise floor failed detected; "
7509999SWang.Lin@Sun.COM 			    "detected %d, threshold %d\n", __func__,
7519999SWang.Lin@Sun.COM 			    nf, nfThresh));
7529999SWang.Lin@Sun.COM 			chan->channelFlags |= CHANNEL_CW_INT;
7539999SWang.Lin@Sun.COM 		}
7549999SWang.Lin@Sun.COM 	}
7559999SWang.Lin@Sun.COM 
7569999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
7579999SWang.Lin@Sun.COM 	h = chan->nfCalHist;
7589999SWang.Lin@Sun.COM #else
7599999SWang.Lin@Sun.COM 	h = ah->nfCalHist;
7609999SWang.Lin@Sun.COM #endif
7619999SWang.Lin@Sun.COM 
7629999SWang.Lin@Sun.COM 	ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
7639999SWang.Lin@Sun.COM 	chan->rawNoiseFloor = h[0].privNF;
7649999SWang.Lin@Sun.COM 
7659999SWang.Lin@Sun.COM 	return (chan->rawNoiseFloor);
7669999SWang.Lin@Sun.COM }
7679999SWang.Lin@Sun.COM 
7689999SWang.Lin@Sun.COM void
ath9k_init_nfcal_hist_buffer(struct ath_hal * ah)7699999SWang.Lin@Sun.COM ath9k_init_nfcal_hist_buffer(struct ath_hal *ah)
7709999SWang.Lin@Sun.COM {
7719999SWang.Lin@Sun.COM 	int i, j;
772*11729SWang.Lin@Sun.COM 	int16_t noise_floor;
773*11729SWang.Lin@Sun.COM 
774*11729SWang.Lin@Sun.COM 	if (AR_SREV_9280(ah))
775*11729SWang.Lin@Sun.COM 		noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
776*11729SWang.Lin@Sun.COM 	else if (AR_SREV_9285(ah))
777*11729SWang.Lin@Sun.COM 		noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
778*11729SWang.Lin@Sun.COM 	else
779*11729SWang.Lin@Sun.COM 		noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE;
7809999SWang.Lin@Sun.COM 
7819999SWang.Lin@Sun.COM 	for (i = 0; i < NUM_NF_READINGS; i++) {
7829999SWang.Lin@Sun.COM 		ah->nfCalHist[i].currIndex = 0;
783*11729SWang.Lin@Sun.COM 		ah->nfCalHist[i].privNF = noise_floor;
7849999SWang.Lin@Sun.COM 		ah->nfCalHist[i].invalidNFcount =
7859999SWang.Lin@Sun.COM 		    AR_PHY_CCA_FILTERWINDOW_LENGTH;
7869999SWang.Lin@Sun.COM 		for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
787*11729SWang.Lin@Sun.COM 			ah->nfCalHist[i].nfCalBuffer[j] = noise_floor;
7889999SWang.Lin@Sun.COM 		}
7899999SWang.Lin@Sun.COM 	}
7909999SWang.Lin@Sun.COM }
7919999SWang.Lin@Sun.COM 
7929999SWang.Lin@Sun.COM signed short
ath9k_hw_getchan_noise(struct ath_hal * ah,struct ath9k_channel * chan)7939999SWang.Lin@Sun.COM ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan)
7949999SWang.Lin@Sun.COM {
7959999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan;
7969999SWang.Lin@Sun.COM 	signed short nf;
7979999SWang.Lin@Sun.COM 
7989999SWang.Lin@Sun.COM 	ichan = ath9k_regd_check_channel(ah, chan);
7999999SWang.Lin@Sun.COM 	if (ichan == NULL) {
8009999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
8019999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
8029999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
8039999SWang.Lin@Sun.COM 		return (ATH_DEFAULT_NOISE_FLOOR);
8049999SWang.Lin@Sun.COM 	}
8059999SWang.Lin@Sun.COM 	if (ichan->rawNoiseFloor == 0) {
8069999SWang.Lin@Sun.COM 		enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan);
8079999SWang.Lin@Sun.COM 		nf = NOISE_FLOOR[mode];
8089999SWang.Lin@Sun.COM 	} else
8099999SWang.Lin@Sun.COM 		nf = ichan->rawNoiseFloor;
8109999SWang.Lin@Sun.COM 
8119999SWang.Lin@Sun.COM 	if (!ath9k_hw_nf_in_range(ah, nf))
8129999SWang.Lin@Sun.COM 		nf = ATH_DEFAULT_NOISE_FLOOR;
8139999SWang.Lin@Sun.COM 
8149999SWang.Lin@Sun.COM 	return (nf);
8159999SWang.Lin@Sun.COM }
8169999SWang.Lin@Sun.COM 
8179999SWang.Lin@Sun.COM boolean_t
ath9k_hw_calibrate(struct ath_hal * ah,struct ath9k_channel * chan,uint8_t rxchainmask,boolean_t longcal,boolean_t * isCalDone)8189999SWang.Lin@Sun.COM ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
8199999SWang.Lin@Sun.COM     uint8_t rxchainmask, boolean_t longcal,
8209999SWang.Lin@Sun.COM     boolean_t *isCalDone)
8219999SWang.Lin@Sun.COM {
8229999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
8239999SWang.Lin@Sun.COM 	struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
8249999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
8259999SWang.Lin@Sun.COM 
8269999SWang.Lin@Sun.COM 	*isCalDone = B_TRUE;
8279999SWang.Lin@Sun.COM 
8289999SWang.Lin@Sun.COM 	if (ichan == NULL) {
8299999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL,
8309999SWang.Lin@Sun.COM 		    "%s: invalid channel %u/0x%x; no mapping\n",
8319999SWang.Lin@Sun.COM 		    __func__, chan->channel, chan->channelFlags));
8329999SWang.Lin@Sun.COM 		return (B_FALSE);
8339999SWang.Lin@Sun.COM 	}
8349999SWang.Lin@Sun.COM 
8359999SWang.Lin@Sun.COM 	if (currCal &&
8369999SWang.Lin@Sun.COM 	    (currCal->calState == CAL_RUNNING ||
8379999SWang.Lin@Sun.COM 	    currCal->calState == CAL_WAITING)) {
8389999SWang.Lin@Sun.COM 		ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal,
8399999SWang.Lin@Sun.COM 		    isCalDone);
8409999SWang.Lin@Sun.COM 		if (*isCalDone) {
8419999SWang.Lin@Sun.COM 			ahp->ah_cal_list_curr = currCal = currCal->calNext;
8429999SWang.Lin@Sun.COM 
8439999SWang.Lin@Sun.COM 			if (currCal->calState == CAL_WAITING) {
8449999SWang.Lin@Sun.COM 				*isCalDone = B_FALSE;
8459999SWang.Lin@Sun.COM 				ath9k_hw_reset_calibration(ah, currCal);
8469999SWang.Lin@Sun.COM 			}
8479999SWang.Lin@Sun.COM 		}
8489999SWang.Lin@Sun.COM 	}
8499999SWang.Lin@Sun.COM 
8509999SWang.Lin@Sun.COM 	if (longcal) {
8519999SWang.Lin@Sun.COM 		(void) ath9k_hw_getnf(ah, ichan);
8529999SWang.Lin@Sun.COM 		ath9k_hw_loadnf(ah, ah->ah_curchan);
8539999SWang.Lin@Sun.COM 		ath9k_hw_start_nfcal(ah);
8549999SWang.Lin@Sun.COM 
8559999SWang.Lin@Sun.COM 		if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
8569999SWang.Lin@Sun.COM 			chan->channelFlags |= CHANNEL_CW_INT;
8579999SWang.Lin@Sun.COM 			ichan->channelFlags &= ~CHANNEL_CW_INT;
8589999SWang.Lin@Sun.COM 		}
8599999SWang.Lin@Sun.COM 	}
8609999SWang.Lin@Sun.COM 
8619999SWang.Lin@Sun.COM 	return (B_TRUE);
8629999SWang.Lin@Sun.COM }
8639999SWang.Lin@Sun.COM 
8649999SWang.Lin@Sun.COM /* AR9285 */
8659999SWang.Lin@Sun.COM static inline void
ath9k_hw_9285_pa_cal(struct ath_hal * ah)8669999SWang.Lin@Sun.COM ath9k_hw_9285_pa_cal(struct ath_hal *ah)
8679999SWang.Lin@Sun.COM {
8689999SWang.Lin@Sun.COM 
8699999SWang.Lin@Sun.COM 	uint32_t regVal;
8709999SWang.Lin@Sun.COM 	int i, offset, offs_6_1, offs_0;
8719999SWang.Lin@Sun.COM 	uint32_t ccomp_org, reg_field;
8729999SWang.Lin@Sun.COM 	uint32_t regList[][2] = {
8739999SWang.Lin@Sun.COM 	    { 0x786c, 0 },
8749999SWang.Lin@Sun.COM 	    { 0x7854, 0 },
8759999SWang.Lin@Sun.COM 	    { 0x7820, 0 },
8769999SWang.Lin@Sun.COM 	    { 0x7824, 0 },
8779999SWang.Lin@Sun.COM 	    { 0x7868, 0 },
8789999SWang.Lin@Sun.COM 	    { 0x783c, 0 },
8799999SWang.Lin@Sun.COM 	    { 0x7838, 0 },
8809999SWang.Lin@Sun.COM 	};
8819999SWang.Lin@Sun.COM 
8829999SWang.Lin@Sun.COM 	if (AR_SREV_9285_11(ah)) {
8839999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
8849999SWang.Lin@Sun.COM 		drv_usecwait(10);
8859999SWang.Lin@Sun.COM 	}
8869999SWang.Lin@Sun.COM 
8879999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regList); i++)
8889999SWang.Lin@Sun.COM 		regList[i][1] = REG_READ(ah, regList[i][0]);
8899999SWang.Lin@Sun.COM 
8909999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x7834);
8919999SWang.Lin@Sun.COM 	regVal &= (~(0x1));
8929999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x7834, regVal);
8939999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x9808);
8949999SWang.Lin@Sun.COM 	regVal |= (0x1 << 27);
8959999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x9808, regVal);
8969999SWang.Lin@Sun.COM 
8979999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
8989999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
8999999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
9009999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
9019999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
9029999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
9039999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
9049999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 1);
9059999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
9069999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
9079999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
9089999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
9099999SWang.Lin@Sun.COM 	ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP);
9109999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 7);
9119999SWang.Lin@Sun.COM 
9129999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0);
9139999SWang.Lin@Sun.COM 	drv_usecwait(30);
9149999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0);
9159999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0);
9169999SWang.Lin@Sun.COM 
9179999SWang.Lin@Sun.COM 	for (i = 6; i > 0; i--) {
9189999SWang.Lin@Sun.COM 		regVal = REG_READ(ah, 0x7834);
9199999SWang.Lin@Sun.COM 		regVal |= (1 << (19 + i));
9209999SWang.Lin@Sun.COM 		REG_WRITE(ah, 0x7834, regVal);
9219999SWang.Lin@Sun.COM 		drv_usecwait(1);
9229999SWang.Lin@Sun.COM 		regVal = REG_READ(ah, 0x7834);
9239999SWang.Lin@Sun.COM 		regVal &= (~(0x1 << (19 + i)));
9249999SWang.Lin@Sun.COM 		reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9);
9259999SWang.Lin@Sun.COM 		regVal |= (reg_field << (19 + i));
9269999SWang.Lin@Sun.COM 		REG_WRITE(ah, 0x7834, regVal);
9279999SWang.Lin@Sun.COM 	}
9289999SWang.Lin@Sun.COM 
9299999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1);
9309999SWang.Lin@Sun.COM 	drv_usecwait(1);
9319999SWang.Lin@Sun.COM 	reg_field = MS(REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9);
9329999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field);
9339999SWang.Lin@Sun.COM 	offs_6_1 = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS);
9349999SWang.Lin@Sun.COM 	offs_0   = MS(REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP);
9359999SWang.Lin@Sun.COM 
9369999SWang.Lin@Sun.COM 	offset = (offs_6_1<<1) | offs_0;
9379999SWang.Lin@Sun.COM 	offset = offset - 0;
9389999SWang.Lin@Sun.COM 	offs_6_1 = offset>>1;
9399999SWang.Lin@Sun.COM 	offs_0 = offset & 1;
9409999SWang.Lin@Sun.COM 
9419999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1);
9429999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0);
9439999SWang.Lin@Sun.COM 
9449999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x7834);
9459999SWang.Lin@Sun.COM 	regVal |= 0x1;
9469999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x7834, regVal);
9479999SWang.Lin@Sun.COM 	regVal = REG_READ(ah, 0x9808);
9489999SWang.Lin@Sun.COM 	regVal &= (~(0x1 << 27));
9499999SWang.Lin@Sun.COM 	REG_WRITE(ah, 0x9808, regVal);
9509999SWang.Lin@Sun.COM 
9519999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regList); i++)
9529999SWang.Lin@Sun.COM 		REG_WRITE(ah, regList[i][0], regList[i][1]);
9539999SWang.Lin@Sun.COM 
9549999SWang.Lin@Sun.COM 	REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org);
9559999SWang.Lin@Sun.COM 
9569999SWang.Lin@Sun.COM 	if (AR_SREV_9285_11(ah))
9579999SWang.Lin@Sun.COM 		REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT);
9589999SWang.Lin@Sun.COM 
9599999SWang.Lin@Sun.COM }
9609999SWang.Lin@Sun.COM 
9619999SWang.Lin@Sun.COM boolean_t
ath9k_hw_init_cal(struct ath_hal * ah,struct ath9k_channel * chan)9629999SWang.Lin@Sun.COM ath9k_hw_init_cal(struct ath_hal *ah,
9639999SWang.Lin@Sun.COM     struct ath9k_channel *chan)
9649999SWang.Lin@Sun.COM {
9659999SWang.Lin@Sun.COM 	struct ath_hal_5416 *ahp = AH5416(ah);
9669999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
9679999SWang.Lin@Sun.COM 
9689999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
9699999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_AGC_CONTROL) |
9709999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_CAL);
9719999SWang.Lin@Sun.COM 
9729999SWang.Lin@Sun.COM 	if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
9739999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE,
9749999SWang.Lin@Sun.COM 		    "%s: offset calibration failed to complete in 1ms; "
9759999SWang.Lin@Sun.COM 		    "noisy environment?\n", __func__));
9769999SWang.Lin@Sun.COM 		return (B_FALSE);
9779999SWang.Lin@Sun.COM 	}
9789999SWang.Lin@Sun.COM 
9799999SWang.Lin@Sun.COM 	if (AR_SREV_9285(ah) && AR_SREV_9285_11_OR_LATER(ah))
9809999SWang.Lin@Sun.COM 			ath9k_hw_9285_pa_cal(ah);
9819999SWang.Lin@Sun.COM 
9829999SWang.Lin@Sun.COM 	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
9839999SWang.Lin@Sun.COM 	    REG_READ(ah, AR_PHY_AGC_CONTROL) |
9849999SWang.Lin@Sun.COM 	    AR_PHY_AGC_CONTROL_NF);
9859999SWang.Lin@Sun.COM 
9869999SWang.Lin@Sun.COM 	ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL;
9879999SWang.Lin@Sun.COM 
9889999SWang.Lin@Sun.COM 	if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) {
9899999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) {
9909999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
9919999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_adcGainCalData);
9929999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
9939999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_adcGainCalData);
9949999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
9959999SWang.Lin@Sun.COM 			    "%s: enabling ADC Gain Calibration.\n",
9969999SWang.Lin@Sun.COM 			    __func__));
9979999SWang.Lin@Sun.COM 		}
9989999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) {
9999999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
10009999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_adcDcCalData);
10019999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
10029999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_adcDcCalData);
10039999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
10049999SWang.Lin@Sun.COM 			    "%s: enabling ADC DC Calibration.\n",
10059999SWang.Lin@Sun.COM 			    __func__));
10069999SWang.Lin@Sun.COM 		}
10079999SWang.Lin@Sun.COM 		if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) {
10089999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
10099999SWang.Lin@Sun.COM 			INIT_CAL(&ahp->ah_iqCalData);
10109999SWang.Lin@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
10119999SWang.Lin@Sun.COM 			INSERT_CAL(ahp, &ahp->ah_iqCalData);
10129999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE,
10139999SWang.Lin@Sun.COM 			    "%s: enabling IQ Calibration.\n",
10149999SWang.Lin@Sun.COM 			    __func__));
10159999SWang.Lin@Sun.COM 		}
10169999SWang.Lin@Sun.COM 
10179999SWang.Lin@Sun.COM 		ahp->ah_cal_list_curr = ahp->ah_cal_list;
10189999SWang.Lin@Sun.COM 
10199999SWang.Lin@Sun.COM 		if (ahp->ah_cal_list_curr)
10209999SWang.Lin@Sun.COM 			ath9k_hw_reset_calibration(ah, ahp->ah_cal_list_curr);
10219999SWang.Lin@Sun.COM 	}
10229999SWang.Lin@Sun.COM 
10239999SWang.Lin@Sun.COM 	ichan->CalValid = 0;
10249999SWang.Lin@Sun.COM 
10259999SWang.Lin@Sun.COM 	return (B_TRUE);
10269999SWang.Lin@Sun.COM }
10279999SWang.Lin@Sun.COM 
10289999SWang.Lin@Sun.COM const struct hal_percal_data iq_cal_multi_sample = {
10299999SWang.Lin@Sun.COM 	IQ_MISMATCH_CAL,
10309999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
10319999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
10329999SWang.Lin@Sun.COM 	ath9k_hw_iqcal_collect,
10339999SWang.Lin@Sun.COM 	ath9k_hw_iqcalibrate
10349999SWang.Lin@Sun.COM };
10359999SWang.Lin@Sun.COM const struct hal_percal_data iq_cal_single_sample = {
10369999SWang.Lin@Sun.COM 	IQ_MISMATCH_CAL,
10379999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
10389999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
10399999SWang.Lin@Sun.COM 	ath9k_hw_iqcal_collect,
10409999SWang.Lin@Sun.COM 	ath9k_hw_iqcalibrate
10419999SWang.Lin@Sun.COM };
10429999SWang.Lin@Sun.COM const struct hal_percal_data adc_gain_cal_multi_sample = {
10439999SWang.Lin@Sun.COM 	ADC_GAIN_CAL,
10449999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
10459999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
10469999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_collect,
10479999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_calibrate
10489999SWang.Lin@Sun.COM };
10499999SWang.Lin@Sun.COM const struct hal_percal_data adc_gain_cal_single_sample = {
10509999SWang.Lin@Sun.COM 	ADC_GAIN_CAL,
10519999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
10529999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
10539999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_collect,
10549999SWang.Lin@Sun.COM 	ath9k_hw_adc_gaincal_calibrate
10559999SWang.Lin@Sun.COM };
10569999SWang.Lin@Sun.COM const struct hal_percal_data adc_dc_cal_multi_sample = {
10579999SWang.Lin@Sun.COM 	ADC_DC_CAL,
10589999SWang.Lin@Sun.COM 	MAX_CAL_SAMPLES,
10599999SWang.Lin@Sun.COM 	PER_MIN_LOG_COUNT,
10609999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
10619999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
10629999SWang.Lin@Sun.COM };
10639999SWang.Lin@Sun.COM const struct hal_percal_data adc_dc_cal_single_sample = {
10649999SWang.Lin@Sun.COM 	ADC_DC_CAL,
10659999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
10669999SWang.Lin@Sun.COM 	PER_MAX_LOG_COUNT,
10679999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
10689999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
10699999SWang.Lin@Sun.COM };
10709999SWang.Lin@Sun.COM const struct hal_percal_data adc_init_dc_cal = {
10719999SWang.Lin@Sun.COM 	ADC_DC_INIT_CAL,
10729999SWang.Lin@Sun.COM 	MIN_CAL_SAMPLES,
10739999SWang.Lin@Sun.COM 	INIT_LOG_COUNT,
10749999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_collect,
10759999SWang.Lin@Sun.COM 	ath9k_hw_adc_dccal_calibrate
10769999SWang.Lin@Sun.COM };
1077