Home | History | Annotate | Line # | Download | only in ar5416
      1  1.1     alc /*
      2  1.1     alc  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
      3  1.1     alc  * Copyright (c) 2002-2008 Atheros Communications, Inc.
      4  1.1     alc  *
      5  1.1     alc  * Permission to use, copy, modify, and/or distribute this software for any
      6  1.1     alc  * purpose with or without fee is hereby granted, provided that the above
      7  1.1     alc  * copyright notice and this permission notice appear in all copies.
      8  1.1     alc  *
      9  1.1     alc  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  1.1     alc  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  1.1     alc  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  1.1     alc  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  1.1     alc  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  1.1     alc  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  1.1     alc  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  1.1     alc  *
     17  1.2  cegger  * $Id: ar5416_cal.c,v 1.2 2011/03/07 11:25:44 cegger Exp $
     18  1.1     alc  */
     19  1.1     alc #include "opt_ah.h"
     20  1.1     alc 
     21  1.1     alc #include "ah.h"
     22  1.1     alc #include "ah_internal.h"
     23  1.1     alc #include "ah_devid.h"
     24  1.1     alc 
     25  1.1     alc #include "ah_eeprom_v14.h"
     26  1.1     alc 
     27  1.2  cegger #include "ar5212/ar5212.h"	/* for NF cal related declarations */
     28  1.2  cegger 
     29  1.1     alc #include "ar5416/ar5416.h"
     30  1.1     alc #include "ar5416/ar5416reg.h"
     31  1.1     alc #include "ar5416/ar5416phy.h"
     32  1.1     alc 
     33  1.1     alc /* Owl specific stuff */
     34  1.1     alc #define NUM_NOISEFLOOR_READINGS 6       /* 3 chains * (ctl + ext) */
     35  1.1     alc 
     36  1.1     alc static void ar5416StartNFCal(struct ath_hal *ah);
     37  1.1     alc static void ar5416LoadNF(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *);
     38  1.1     alc static int16_t ar5416GetNf(struct ath_hal *, HAL_CHANNEL_INTERNAL *);
     39  1.1     alc 
     40  1.1     alc /*
     41  1.1     alc  * Determine if calibration is supported by device and channel flags
     42  1.1     alc  */
     43  1.1     alc static OS_INLINE HAL_BOOL
     44  1.1     alc ar5416IsCalSupp(struct ath_hal *ah, HAL_CHANNEL *chan, HAL_CAL_TYPE calType)
     45  1.1     alc {
     46  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
     47  1.1     alc 
     48  1.1     alc 	switch (calType & cal->suppCals) {
     49  1.1     alc 	case IQ_MISMATCH_CAL:
     50  1.1     alc 		/* Run IQ Mismatch for non-CCK only */
     51  1.1     alc 		return !IS_CHAN_B(chan);
     52  1.1     alc 	case ADC_GAIN_CAL:
     53  1.1     alc 	case ADC_DC_CAL:
     54  1.1     alc 		/* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
     55  1.1     alc 		return !IS_CHAN_B(chan) &&
     56  1.1     alc 		    !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan));
     57  1.1     alc 	}
     58  1.1     alc 	return AH_FALSE;
     59  1.1     alc }
     60  1.1     alc 
     61  1.1     alc /*
     62  1.1     alc  * Setup HW to collect samples used for current cal
     63  1.1     alc  */
     64  1.1     alc static void
     65  1.1     alc ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
     66  1.1     alc {
     67  1.1     alc 	/* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
     68  1.1     alc 	OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
     69  1.1     alc 	    AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
     70  1.1     alc 	    currCal->calData->calCountMax);
     71  1.1     alc 
     72  1.1     alc 	/* Select calibration to run */
     73  1.1     alc 	switch (currCal->calData->calType) {
     74  1.1     alc 	case IQ_MISMATCH_CAL:
     75  1.1     alc 		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
     76  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
     77  1.1     alc 		    "%s: start IQ Mismatch calibration\n", __func__);
     78  1.1     alc 		break;
     79  1.1     alc 	case ADC_GAIN_CAL:
     80  1.1     alc 		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
     81  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
     82  1.1     alc 		    "%s: start ADC Gain calibration\n", __func__);
     83  1.1     alc 		break;
     84  1.1     alc 	case ADC_DC_CAL:
     85  1.1     alc 		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
     86  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
     87  1.1     alc 		    "%s: start ADC DC calibration\n", __func__);
     88  1.1     alc 		break;
     89  1.1     alc 	case ADC_DC_INIT_CAL:
     90  1.1     alc 		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
     91  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
     92  1.1     alc 		    "%s: start Init ADC DC calibration\n", __func__);
     93  1.1     alc 		break;
     94  1.1     alc 	}
     95  1.1     alc 	/* Kick-off cal */
     96  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);
     97  1.1     alc }
     98  1.1     alc 
     99  1.1     alc /*
    100  1.1     alc  * Initialize shared data structures and prepare a cal to be run.
    101  1.1     alc  */
    102  1.1     alc static void
    103  1.1     alc ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
    104  1.1     alc {
    105  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    106  1.1     alc 
    107  1.1     alc 	/* Reset data structures shared between different calibrations */
    108  1.1     alc 	OS_MEMZERO(cal->caldata, sizeof(cal->caldata));
    109  1.1     alc 	cal->calSamples = 0;
    110  1.1     alc 
    111  1.1     alc 	/* Setup HW for new calibration */
    112  1.1     alc 	ar5416SetupMeasurement(ah, currCal);
    113  1.1     alc 
    114  1.1     alc 	/* Change SW state to RUNNING for this calibration */
    115  1.1     alc 	currCal->calState = CAL_RUNNING;
    116  1.1     alc }
    117  1.1     alc 
    118  1.1     alc #if 0
    119  1.1     alc /*
    120  1.1     alc  * Run non-periodic calibrations.
    121  1.1     alc  */
    122  1.1     alc static HAL_BOOL
    123  1.1     alc ar5416RunInitCals(struct ath_hal *ah, int init_cal_count)
    124  1.1     alc {
    125  1.1     alc 	struct ath_hal_5416 *ahp = AH5416(ah);
    126  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    127  1.1     alc 	HAL_CHANNEL_INTERNAL ichan;	/* XXX bogus */
    128  1.1     alc 	HAL_CAL_LIST *curCal = ahp->ah_cal_curr;
    129  1.1     alc 	HAL_BOOL isCalDone;
    130  1.1     alc 	int i;
    131  1.1     alc 
    132  1.1     alc 	if (curCal == AH_NULL)
    133  1.1     alc 		return AH_FALSE;
    134  1.1     alc 
    135  1.1     alc 	ichan.calValid = 0;
    136  1.1     alc 	for (i = 0; i < init_cal_count; i++) {
    137  1.1     alc 		/* Reset this Cal */
    138  1.1     alc 		ar5416ResetMeasurement(ah, curCal);
    139  1.1     alc 		/* Poll for offset calibration complete */
    140  1.1     alc 		if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
    141  1.1     alc 			HALDEBUG(ah, HAL_DEBUG_ANY,
    142  1.1     alc 			    "%s: Cal %d failed to finish in 100ms.\n",
    143  1.1     alc 			    __func__, curCal->calData->calType);
    144  1.1     alc 			/* Re-initialize list pointers for periodic cals */
    145  1.1     alc 			cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
    146  1.1     alc 			return AH_FALSE;
    147  1.1     alc 		}
    148  1.1     alc 		/* Run this cal */
    149  1.1     alc 		ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,
    150  1.1     alc 		    curCal, &isCalDone);
    151  1.1     alc 		if (!isCalDone)
    152  1.1     alc 			HALDEBUG(ah, HAL_DEBUG_ANY,
    153  1.1     alc 			    "%s: init cal %d did not complete.\n",
    154  1.1     alc 			    __func__, curCal->calData->calType);
    155  1.1     alc 		if (curCal->calNext != AH_NULL)
    156  1.1     alc 			curCal = curCal->calNext;
    157  1.1     alc 	}
    158  1.1     alc 
    159  1.1     alc 	/* Re-initialize list pointers for periodic cals */
    160  1.1     alc 	cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
    161  1.1     alc 	return AH_TRUE;
    162  1.1     alc }
    163  1.1     alc #endif
    164  1.1     alc 
    165  1.1     alc /*
    166  1.1     alc  * Initialize Calibration infrastructure.
    167  1.1     alc  */
    168  1.1     alc HAL_BOOL
    169  1.1     alc ar5416InitCal(struct ath_hal *ah, HAL_CHANNEL *chan)
    170  1.1     alc {
    171  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    172  1.1     alc 	HAL_CHANNEL_INTERNAL *ichan;
    173  1.1     alc 
    174  1.1     alc 	ichan = ath_hal_checkchannel(ah, chan);
    175  1.1     alc 	HALASSERT(ichan != AH_NULL);
    176  1.1     alc 
    177  1.1     alc 	if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
    178  1.1     alc 		/* Enable Rx Filter Cal */
    179  1.1     alc 		OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
    180  1.1     alc 		OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
    181  1.1     alc 		    AR_PHY_AGC_CONTROL_FLTR_CAL);
    182  1.1     alc 
    183  1.1     alc 		/* Clear the carrier leak cal bit */
    184  1.1     alc 		OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
    185  1.1     alc 
    186  1.1     alc 		/* kick off the cal */
    187  1.1     alc 		OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
    188  1.1     alc 
    189  1.1     alc 		/* Poll for offset calibration complete */
    190  1.1     alc 		if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
    191  1.1     alc 			HALDEBUG(ah, HAL_DEBUG_ANY,
    192  1.1     alc 			    "%s: offset calibration failed to complete in 1ms; "
    193  1.1     alc 			    "noisy environment?\n", __func__);
    194  1.1     alc 			return AH_FALSE;
    195  1.1     alc 		}
    196  1.1     alc 
    197  1.1     alc 		/* Set the cl cal bit and rerun the cal a 2nd time */
    198  1.1     alc 		/* Enable Rx Filter Cal */
    199  1.1     alc 		OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
    200  1.1     alc 		OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
    201  1.1     alc 		    AR_PHY_AGC_CONTROL_FLTR_CAL);
    202  1.1     alc 
    203  1.1     alc 		OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
    204  1.1     alc 	}
    205  1.1     alc 
    206  1.1     alc 	/* Calibrate the AGC */
    207  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
    208  1.1     alc 
    209  1.1     alc 	/* Poll for offset calibration complete */
    210  1.1     alc 	if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
    211  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    212  1.1     alc 		    "%s: offset calibration did not complete in 1ms; "
    213  1.1     alc 		    "noisy environment?\n", __func__);
    214  1.1     alc 		return AH_FALSE;
    215  1.1     alc 	}
    216  1.1     alc 
    217  1.1     alc 	/*
    218  1.1     alc 	 * Do NF calibration after DC offset and other CALs.
    219  1.1     alc 	 * Per system engineers, noise floor value can sometimes be 20 dB
    220  1.1     alc 	 * higher than normal value if DC offset and noise floor cal are
    221  1.1     alc 	 * triggered at the same time.
    222  1.1     alc 	 */
    223  1.2  cegger 	/* XXX this actually kicks off a NF calibration -adrian */
    224  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
    225  1.2  cegger 	/*
    226  1.2  cegger 	 * Try to make sure the above NF cal completes, just so
    227  1.2  cegger 	 * it doesn't clash with subsequent percals - adrian
    228  1.2  cegger 	 */
    229  1.2  cegger 	if (!ar5212WaitNFCalComplete(ah, 10000)) {
    230  1.2  cegger 		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: initial NF calibration did "
    231  1.2  cegger 		    "not complete in time; noisy environment?\n", __func__);
    232  1.2  cegger 		return AH_FALSE;
    233  1.2  cegger 	}
    234  1.1     alc 
    235  1.1     alc 	/* Initialize list pointers */
    236  1.1     alc 	cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
    237  1.1     alc 
    238  1.1     alc 	/*
    239  1.1     alc 	 * Enable IQ, ADC Gain, ADC DC Offset Cals
    240  1.1     alc 	 */
    241  1.1     alc 	if (AR_SREV_SOWL_10_OR_LATER(ah)) {
    242  1.1     alc 		/* Setup all non-periodic, init time only calibrations */
    243  1.1     alc 		/* XXX: Init DC Offset not working yet */
    244  1.1     alc #if 0
    245  1.1     alc 		if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
    246  1.1     alc 			INIT_CAL(&cal->adcDcCalInitData);
    247  1.1     alc 			INSERT_CAL(cal, &cal->adcDcCalInitData);
    248  1.1     alc 		}
    249  1.1     alc 		/* Initialize current pointer to first element in list */
    250  1.1     alc 		cal->cal_curr = cal->cal_list;
    251  1.1     alc 
    252  1.1     alc 		if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
    253  1.1     alc 			return AH_FALSE;
    254  1.1     alc #endif
    255  1.1     alc 	}
    256  1.1     alc 
    257  1.1     alc 	/* If Cals are supported, add them to list via INIT/INSERT_CAL */
    258  1.1     alc 	if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
    259  1.1     alc 		INIT_CAL(&cal->adcGainCalData);
    260  1.1     alc 		INSERT_CAL(cal, &cal->adcGainCalData);
    261  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
    262  1.1     alc 		    "%s: enable ADC Gain Calibration.\n", __func__);
    263  1.1     alc 	}
    264  1.1     alc 	if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
    265  1.1     alc 		INIT_CAL(&cal->adcDcCalData);
    266  1.1     alc 		INSERT_CAL(cal, &cal->adcDcCalData);
    267  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
    268  1.1     alc 		    "%s: enable ADC DC Calibration.\n", __func__);
    269  1.1     alc 	}
    270  1.1     alc 	if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
    271  1.1     alc 		INIT_CAL(&cal->iqCalData);
    272  1.1     alc 		INSERT_CAL(cal, &cal->iqCalData);
    273  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_PERCAL,
    274  1.1     alc 		    "%s: enable IQ Calibration.\n", __func__);
    275  1.1     alc 	}
    276  1.1     alc 	/* Initialize current pointer to first element in list */
    277  1.1     alc 	cal->cal_curr = cal->cal_list;
    278  1.1     alc 
    279  1.1     alc 	/* Kick off measurements for the first cal */
    280  1.1     alc 	if (cal->cal_curr != AH_NULL)
    281  1.1     alc 		ar5416ResetMeasurement(ah, cal->cal_curr);
    282  1.1     alc 
    283  1.1     alc 	/* Mark all calibrations on this channel as being invalid */
    284  1.1     alc 	ichan->calValid = 0;
    285  1.1     alc 
    286  1.1     alc 	return AH_TRUE;
    287  1.1     alc }
    288  1.1     alc 
    289  1.1     alc /*
    290  1.1     alc  * Entry point for upper layers to restart current cal.
    291  1.1     alc  * Reset the calibration valid bit in channel.
    292  1.1     alc  */
    293  1.1     alc HAL_BOOL
    294  1.1     alc ar5416ResetCalValid(struct ath_hal *ah, HAL_CHANNEL *chan)
    295  1.1     alc {
    296  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    297  1.1     alc 	HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
    298  1.1     alc 	HAL_CAL_LIST *currCal = cal->cal_curr;
    299  1.1     alc 
    300  1.1     alc 	if (!AR_SREV_SOWL_10_OR_LATER(ah))
    301  1.1     alc 		return AH_FALSE;
    302  1.1     alc 	if (currCal == AH_NULL)
    303  1.1     alc 		return AH_FALSE;
    304  1.1     alc 	if (ichan == AH_NULL) {
    305  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    306  1.1     alc 		    "%s: invalid channel %u/0x%x; no mapping\n",
    307  1.1     alc 		    __func__, chan->channel, chan->channelFlags);
    308  1.1     alc 		return AH_FALSE;
    309  1.1     alc 	}
    310  1.1     alc 	/*
    311  1.1     alc 	 * Expected that this calibration has run before, post-reset.
    312  1.1     alc 	 * Current state should be done
    313  1.1     alc 	 */
    314  1.1     alc 	if (currCal->calState != CAL_DONE) {
    315  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    316  1.1     alc 		    "%s: Calibration state incorrect, %d\n",
    317  1.1     alc 		    __func__, currCal->calState);
    318  1.1     alc 		return AH_FALSE;
    319  1.1     alc 	}
    320  1.1     alc 
    321  1.1     alc 	/* Verify Cal is supported on this channel */
    322  1.1     alc 	if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
    323  1.1     alc 		return AH_FALSE;
    324  1.1     alc 
    325  1.1     alc 	HALDEBUG(ah, HAL_DEBUG_PERCAL,
    326  1.1     alc 	    "%s: Resetting Cal %d state for channel %u/0x%x\n",
    327  1.1     alc 	    __func__, currCal->calData->calType, chan->channel,
    328  1.1     alc 	    chan->channelFlags);
    329  1.1     alc 
    330  1.1     alc 	/* Disable cal validity in channel */
    331  1.1     alc 	ichan->calValid &= ~currCal->calData->calType;
    332  1.1     alc 	currCal->calState = CAL_WAITING;
    333  1.1     alc 
    334  1.1     alc 	return AH_TRUE;
    335  1.1     alc }
    336  1.1     alc 
    337  1.1     alc /*
    338  1.1     alc  * Recalibrate the lower PHY chips to account for temperature/environment
    339  1.1     alc  * changes.
    340  1.1     alc  */
    341  1.1     alc static void
    342  1.1     alc ar5416DoCalibration(struct ath_hal *ah,  HAL_CHANNEL_INTERNAL *ichan,
    343  1.1     alc 	uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
    344  1.1     alc {
    345  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    346  1.1     alc 
    347  1.1     alc 	/* Cal is assumed not done until explicitly set below */
    348  1.1     alc 	*isCalDone = AH_FALSE;
    349  1.1     alc 
    350  1.1     alc 	HALDEBUG(ah, HAL_DEBUG_PERCAL,
    351  1.1     alc 	    "%s: %s Calibration, state %d, calValid 0x%x\n",
    352  1.1     alc 	    __func__, currCal->calData->calName, currCal->calState,
    353  1.1     alc 	    ichan->calValid);
    354  1.1     alc 
    355  1.1     alc 	/* Calibration in progress. */
    356  1.1     alc 	if (currCal->calState == CAL_RUNNING) {
    357  1.1     alc 		/* Check to see if it has finished. */
    358  1.1     alc 		if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
    359  1.1     alc 			HALDEBUG(ah, HAL_DEBUG_PERCAL,
    360  1.1     alc 			    "%s: sample %d of %d finished\n",
    361  1.1     alc 			    __func__, cal->calSamples,
    362  1.1     alc 			    currCal->calData->calNumSamples);
    363  1.1     alc 			/*
    364  1.1     alc 			 * Collect measurements for active chains.
    365  1.1     alc 			 */
    366  1.1     alc 			currCal->calData->calCollect(ah);
    367  1.1     alc 			if (++cal->calSamples >= currCal->calData->calNumSamples) {
    368  1.1     alc 				int i, numChains = 0;
    369  1.1     alc 				for (i = 0; i < AR5416_MAX_CHAINS; i++) {
    370  1.1     alc 					if (rxchainmask & (1 << i))
    371  1.1     alc 						numChains++;
    372  1.1     alc 				}
    373  1.1     alc 				/*
    374  1.1     alc 				 * Process accumulated data
    375  1.1     alc 				 */
    376  1.1     alc 				currCal->calData->calPostProc(ah, numChains);
    377  1.1     alc 
    378  1.1     alc 				/* Calibration has finished. */
    379  1.1     alc 				ichan->calValid |= currCal->calData->calType;
    380  1.1     alc 				currCal->calState = CAL_DONE;
    381  1.1     alc 				*isCalDone = AH_TRUE;
    382  1.1     alc 			} else {
    383  1.1     alc 				/*
    384  1.1     alc 				 * Set-up to collect of another sub-sample.
    385  1.1     alc 				 */
    386  1.1     alc 				ar5416SetupMeasurement(ah, currCal);
    387  1.1     alc 			}
    388  1.1     alc 		}
    389  1.1     alc 	} else if (!(ichan->calValid & currCal->calData->calType)) {
    390  1.1     alc 		/* If current cal is marked invalid in channel, kick it off */
    391  1.1     alc 		ar5416ResetMeasurement(ah, currCal);
    392  1.1     alc 	}
    393  1.1     alc }
    394  1.1     alc 
    395  1.1     alc /*
    396  1.1     alc  * Internal interface to schedule periodic calibration work.
    397  1.1     alc  */
    398  1.1     alc HAL_BOOL
    399  1.1     alc ar5416PerCalibrationN(struct ath_hal *ah,  HAL_CHANNEL *chan,
    400  1.1     alc 	u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
    401  1.1     alc {
    402  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    403  1.1     alc 	HAL_CAL_LIST *currCal = cal->cal_curr;
    404  1.1     alc 	HAL_CHANNEL_INTERNAL *ichan;
    405  1.1     alc 
    406  1.1     alc 	OS_MARK(ah, AH_MARK_PERCAL, chan->channel);
    407  1.1     alc 
    408  1.1     alc 	*isCalDone = AH_TRUE;
    409  1.1     alc 
    410  1.2  cegger 	/*
    411  1.2  cegger 	 * Since ath_hal calls the PerCal method with rxchainmask=0x1;
    412  1.2  cegger 	 * override it with the current chainmask. The upper levels currently
    413  1.2  cegger 	 * doesn't know about the chainmask.
    414  1.2  cegger 	 */
    415  1.2  cegger 	rxchainmask = AH5416(ah)->ah_rx_chainmask;
    416  1.2  cegger 
    417  1.1     alc 	/* Invalid channel check */
    418  1.1     alc 	ichan = ath_hal_checkchannel(ah, chan);
    419  1.1     alc 	if (ichan == AH_NULL) {
    420  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    421  1.1     alc 		    "%s: invalid channel %u/0x%x; no mapping\n",
    422  1.1     alc 		    __func__, chan->channel, chan->channelFlags);
    423  1.1     alc 		return AH_FALSE;
    424  1.1     alc 	}
    425  1.1     alc 
    426  1.1     alc 	/*
    427  1.1     alc 	 * For given calibration:
    428  1.1     alc 	 * 1. Call generic cal routine
    429  1.1     alc 	 * 2. When this cal is done (isCalDone) if we have more cals waiting
    430  1.1     alc 	 *    (eg after reset), mask this to upper layers by not propagating
    431  1.1     alc 	 *    isCalDone if it is set to TRUE.
    432  1.1     alc 	 *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
    433  1.1     alc 	 *    to be run.
    434  1.1     alc 	 */
    435  1.1     alc 	if (currCal != AH_NULL &&
    436  1.1     alc 	    (currCal->calState == CAL_RUNNING ||
    437  1.1     alc 	     currCal->calState == CAL_WAITING)) {
    438  1.1     alc 		ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
    439  1.1     alc 		if (*isCalDone == AH_TRUE) {
    440  1.1     alc 			cal->cal_curr = currCal = currCal->calNext;
    441  1.1     alc 			if (currCal->calState == CAL_WAITING) {
    442  1.1     alc 				*isCalDone = AH_FALSE;
    443  1.1     alc 				ar5416ResetMeasurement(ah, currCal);
    444  1.1     alc 			}
    445  1.1     alc 		}
    446  1.1     alc 	}
    447  1.1     alc 
    448  1.1     alc 	/* Do NF cal only at longer intervals */
    449  1.1     alc 	if (longcal) {
    450  1.1     alc 		/*
    451  1.1     alc 		 * Get the value from the previous NF cal
    452  1.1     alc 		 * and update the history buffer.
    453  1.1     alc 		 */
    454  1.1     alc 		ar5416GetNf(ah, ichan);
    455  1.1     alc 
    456  1.1     alc 		/*
    457  1.1     alc 		 * Load the NF from history buffer of the current channel.
    458  1.1     alc 		 * NF is slow time-variant, so it is OK to use a
    459  1.1     alc 		 * historical value.
    460  1.1     alc 		 */
    461  1.1     alc 		ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
    462  1.1     alc 
    463  1.1     alc 		/* start NF calibration, without updating BB NF register*/
    464  1.1     alc 		ar5416StartNFCal(ah);
    465  1.1     alc 	}
    466  1.1     alc 	return AH_TRUE;
    467  1.1     alc }
    468  1.1     alc 
    469  1.1     alc /*
    470  1.1     alc  * Recalibrate the lower PHY chips to account for temperature/environment
    471  1.1     alc  * changes.
    472  1.1     alc  */
    473  1.1     alc HAL_BOOL
    474  1.1     alc ar5416PerCalibration(struct ath_hal *ah,  HAL_CHANNEL *chan, HAL_BOOL *isIQdone)
    475  1.1     alc {
    476  1.1     alc 	struct ath_hal_5416 *ahp = AH5416(ah);
    477  1.1     alc 	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
    478  1.1     alc 	HAL_CAL_LIST *curCal = cal->cal_curr;
    479  1.1     alc 
    480  1.1     alc 	if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
    481  1.1     alc 		return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
    482  1.1     alc 		    AH_TRUE, isIQdone);
    483  1.1     alc 	} else {
    484  1.1     alc 		HAL_BOOL isCalDone;
    485  1.1     alc 
    486  1.1     alc 		*isIQdone = AH_FALSE;
    487  1.1     alc 		return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
    488  1.1     alc 		    AH_TRUE, &isCalDone);
    489  1.1     alc 	}
    490  1.1     alc }
    491  1.1     alc 
    492  1.1     alc static HAL_BOOL
    493  1.1     alc ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
    494  1.1     alc 	const HAL_CHANNEL_INTERNAL *chan, int16_t *nft)
    495  1.1     alc {
    496  1.1     alc 	switch (chan->channelFlags & CHANNEL_ALL_NOTURBO) {
    497  1.1     alc 	case CHANNEL_A:
    498  1.1     alc 	case CHANNEL_A_HT20:
    499  1.1     alc 	case CHANNEL_A_HT40PLUS:
    500  1.1     alc 	case CHANNEL_A_HT40MINUS:
    501  1.1     alc 		ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
    502  1.1     alc 		break;
    503  1.1     alc 	case CHANNEL_B:
    504  1.1     alc 	case CHANNEL_G:
    505  1.1     alc 	case CHANNEL_G_HT20:
    506  1.1     alc 	case CHANNEL_G_HT40PLUS:
    507  1.1     alc 	case CHANNEL_G_HT40MINUS:
    508  1.1     alc 		ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
    509  1.1     alc 		break;
    510  1.1     alc 	default:
    511  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    512  1.1     alc 		    "%s: invalid channel flags 0x%x\n",
    513  1.1     alc 		    __func__, chan->channelFlags);
    514  1.1     alc 		return AH_FALSE;
    515  1.1     alc 	}
    516  1.1     alc 	return AH_TRUE;
    517  1.1     alc }
    518  1.1     alc 
    519  1.1     alc static void
    520  1.1     alc ar5416StartNFCal(struct ath_hal *ah)
    521  1.1     alc {
    522  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
    523  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
    524  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
    525  1.1     alc }
    526  1.1     alc 
    527  1.1     alc static void
    528  1.1     alc ar5416LoadNF(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan)
    529  1.1     alc {
    530  1.1     alc 	static const uint32_t ar5416_cca_regs[] = {
    531  1.1     alc 		AR_PHY_CCA,
    532  1.1     alc 		AR_PHY_CH1_CCA,
    533  1.1     alc 		AR_PHY_CH2_CCA,
    534  1.1     alc 		AR_PHY_EXT_CCA,
    535  1.1     alc 		AR_PHY_CH1_EXT_CCA,
    536  1.1     alc 		AR_PHY_CH2_EXT_CCA
    537  1.1     alc 	};
    538  1.1     alc 	struct ar5212NfCalHist *h;
    539  1.2  cegger 	int i;
    540  1.1     alc 	int32_t val;
    541  1.1     alc 	uint8_t chainmask;
    542  1.1     alc 
    543  1.1     alc 	/*
    544  1.1     alc 	 * Force NF calibration for all chains.
    545  1.1     alc 	 */
    546  1.1     alc 	if (AR_SREV_KITE(ah)) {
    547  1.1     alc 		/* Kite has only one chain */
    548  1.1     alc 		chainmask = 0x9;
    549  1.1     alc 	} else if (AR_SREV_MERLIN(ah)) {
    550  1.1     alc 		/* Merlin has only two chains */
    551  1.1     alc 		chainmask = 0x1B;
    552  1.1     alc 	} else {
    553  1.1     alc 		chainmask = 0x3F;
    554  1.1     alc 	}
    555  1.1     alc 
    556  1.1     alc 	/*
    557  1.1     alc 	 * Write filtered NF values into maxCCApwr register parameter
    558  1.1     alc 	 * so we can load below.
    559  1.1     alc 	 */
    560  1.1     alc 	h = AH5416(ah)->ah_cal.nfCalHist;
    561  1.1     alc 	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++)
    562  1.1     alc 		if (chainmask & (1 << i)) {
    563  1.1     alc 			val = OS_REG_READ(ah, ar5416_cca_regs[i]);
    564  1.1     alc 			val &= 0xFFFFFE00;
    565  1.1     alc 			val |= (((uint32_t)(h[i].privNF) << 1) & 0x1ff);
    566  1.1     alc 			OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
    567  1.1     alc 		}
    568  1.1     alc 
    569  1.1     alc 	/* Load software filtered NF value into baseband internal minCCApwr variable. */
    570  1.1     alc 	OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
    571  1.1     alc 	OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
    572  1.1     alc 	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
    573  1.1     alc 
    574  1.1     alc 	/* Wait for load to complete, should be fast, a few 10s of us. */
    575  1.2  cegger 	if (! ar5212WaitNFCalComplete(ah, 10000)) {
    576  1.2  cegger 		/*
    577  1.2  cegger 		 * We timed out waiting for the noisefloor to load, probably due
    578  1.2  cegger 		 * to an in-progress rx. Simply return here and allow the load
    579  1.2  cegger 		 * plenty of time to complete before the next calibration
    580  1.2  cegger 		 * interval. We need to avoid trying to load -50 (which happens
    581  1.2  cegger 		 * below) while the previous load is still in progress as this
    582  1.2  cegger 		 * can cause rx deafness. Instead by returning here, the
    583  1.2  cegger 		 * baseband nf cal will just be capped by our present
    584  1.2  cegger 		 * noisefloor until the next calibration timer.
    585  1.2  cegger 		 */
    586  1.2  cegger 		HALDEBUG(ah, HAL_DEBUG_ANY, "Timeout while waiting for nf "
    587  1.2  cegger 		    "to load: AR_PHY_AGC_CONTROL=0x%x\n",
    588  1.2  cegger 		    OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
    589  1.2  cegger 		return;
    590  1.1     alc 	}
    591  1.1     alc 
    592  1.1     alc 	/*
    593  1.1     alc 	 * Restore maxCCAPower register parameter again so that we're not capped
    594  1.1     alc 	 * by the median we just loaded.  This will be initial (and max) value
    595  1.1     alc 	 * of next noise floor calibration the baseband does.
    596  1.1     alc 	 */
    597  1.1     alc 	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++)
    598  1.1     alc 		if (chainmask & (1 << i)) {
    599  1.1     alc 			val = OS_REG_READ(ah, ar5416_cca_regs[i]);
    600  1.1     alc 			val &= 0xFFFFFE00;
    601  1.1     alc 			val |= (((uint32_t)(-50) << 1) & 0x1ff);
    602  1.1     alc 			OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
    603  1.1     alc 		}
    604  1.1     alc }
    605  1.1     alc 
    606  1.1     alc void
    607  1.1     alc ar5416InitNfHistBuff(struct ar5212NfCalHist *h)
    608  1.1     alc {
    609  1.1     alc 	int i, j;
    610  1.1     alc 
    611  1.1     alc 	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
    612  1.1     alc 		h[i].currIndex = 0;
    613  1.1     alc 		h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
    614  1.1     alc 		h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
    615  1.1     alc 		for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
    616  1.1     alc 			h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
    617  1.1     alc 	}
    618  1.1     alc }
    619  1.1     alc 
    620  1.1     alc /*
    621  1.1     alc  * Update the noise floor buffer as a ring buffer
    622  1.1     alc  */
    623  1.1     alc static void
    624  1.1     alc ar5416UpdateNFHistBuff(struct ar5212NfCalHist *h, int16_t *nfarray)
    625  1.1     alc {
    626  1.1     alc 	int i;
    627  1.1     alc 
    628  1.1     alc 	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
    629  1.1     alc 		h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
    630  1.1     alc 
    631  1.1     alc 		if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
    632  1.1     alc 			h[i].currIndex = 0;
    633  1.1     alc 		if (h[i].invalidNFcount > 0) {
    634  1.1     alc 			if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
    635  1.1     alc 			    nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
    636  1.1     alc 				h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
    637  1.1     alc 			} else {
    638  1.1     alc 				h[i].invalidNFcount--;
    639  1.1     alc 				h[i].privNF = nfarray[i];
    640  1.1     alc 			}
    641  1.1     alc 		} else {
    642  1.1     alc 			h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
    643  1.1     alc 		}
    644  1.1     alc 	}
    645  1.1     alc }
    646  1.1     alc 
    647  1.1     alc /*
    648  1.1     alc  * Read the NF and check it against the noise floor threshhold
    649  1.1     alc  */
    650  1.1     alc static int16_t
    651  1.1     alc ar5416GetNf(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan)
    652  1.1     alc {
    653  1.1     alc 	int16_t nf, nfThresh;
    654  1.1     alc 
    655  1.1     alc 	if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
    656  1.1     alc 		HALDEBUG(ah, HAL_DEBUG_ANY,
    657  1.1     alc 		    "%s: NF didn't complete in calibration window\n", __func__);
    658  1.1     alc 		nf = 0;
    659  1.1     alc 	} else {
    660  1.1     alc 		/* Finished NF cal, check against threshold */
    661  1.1     alc 		int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
    662  1.1     alc 
    663  1.1     alc 		/* TODO - enhance for multiple chains and ext ch */
    664  1.1     alc 		ath_hal_getNoiseFloor(ah, nfarray);
    665  1.1     alc 		nf = nfarray[0];
    666  1.1     alc 		if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
    667  1.1     alc 			if (nf > nfThresh) {
    668  1.1     alc 				HALDEBUG(ah, HAL_DEBUG_ANY,
    669  1.1     alc 				    "%s: noise floor failed detected; "
    670  1.1     alc 				    "detected %d, threshold %d\n", __func__,
    671  1.1     alc 				    nf, nfThresh);
    672  1.1     alc 				/*
    673  1.1     alc 				 * NB: Don't discriminate 2.4 vs 5Ghz, if this
    674  1.1     alc 				 *     happens it indicates a problem regardless
    675  1.1     alc 				 *     of the band.
    676  1.1     alc 				 */
    677  1.1     alc 				chan->channelFlags |= CHANNEL_CW_INT;
    678  1.1     alc 				nf = 0;
    679  1.1     alc 			}
    680  1.1     alc 		} else {
    681  1.1     alc 			nf = 0;
    682  1.1     alc 		}
    683  1.1     alc 		ar5416UpdateNFHistBuff(AH5416(ah)->ah_cal.nfCalHist, nfarray);
    684  1.1     alc 		chan->rawNoiseFloor = nf;
    685  1.1     alc 	}
    686  1.1     alc 	return nf;
    687  1.1     alc }
    688