1185380Ssam/*
2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3185380Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185380Ssam *
5185380Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185380Ssam * purpose with or without fee is hereby granted, provided that the above
7185380Ssam * copyright notice and this permission notice appear in all copies.
8185380Ssam *
9185380Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185380Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185380Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185380Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185380Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185380Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185380Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185380Ssam *
17187831Ssam * $FreeBSD$
18185380Ssam */
19185380Ssam#include "opt_ah.h"
20185380Ssam
21185380Ssam#include "ah.h"
22185380Ssam#include "ah_internal.h"
23185380Ssam#include "ah_devid.h"
24185380Ssam
25185380Ssam#include "ah_eeprom_v14.h"
26185380Ssam
27211207Sadrian#include "ar5212/ar5212.h"	/* for NF cal related declarations */
28211207Sadrian
29185380Ssam#include "ar5416/ar5416.h"
30185380Ssam#include "ar5416/ar5416reg.h"
31185380Ssam#include "ar5416/ar5416phy.h"
32185380Ssam
33185380Ssam/* Owl specific stuff */
34185380Ssam#define NUM_NOISEFLOOR_READINGS 6       /* 3 chains * (ctl + ext) */
35185380Ssam
36185380Ssamstatic void ar5416StartNFCal(struct ath_hal *ah);
37187831Ssamstatic void ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *);
38187831Ssamstatic int16_t ar5416GetNf(struct ath_hal *, struct ieee80211_channel *);
39185380Ssam
40218068Sadrianstatic uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan);
41218068Sadrianstatic void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf);
42218068Sadrian
43185380Ssam/*
44185380Ssam * Determine if calibration is supported by device and channel flags
45185380Ssam */
46217629Sadrian
47217629Sadrian/*
48217629Sadrian * ADC GAIN/DC offset calibration is for calibrating two ADCs that
49217629Sadrian * are acting as one by interleaving incoming symbols. This isn't
50217629Sadrian * relevant for 2.4GHz 20MHz wide modes because, as far as I can tell,
51217629Sadrian * the secondary ADC is never enabled. It is enabled however for
52217629Sadrian * 5GHz modes.
53217629Sadrian *
54217629Sadrian * It hasn't been confirmed whether doing this calibration is needed
55217629Sadrian * at all in the above modes and/or whether it's actually harmful.
56217629Sadrian * So for now, let's leave it enabled and just remember to get
57217629Sadrian * confirmation that it needs to be clarified.
58217629Sadrian *
59217629Sadrian * See US Patent No: US 7,541,952 B1:
60217629Sadrian *  " Method and Apparatus for Offset and Gain Compensation for
61217629Sadrian *    Analog-to-Digital Converters."
62217629Sadrian */
63185380Ssamstatic OS_INLINE HAL_BOOL
64187831Ssamar5416IsCalSupp(struct ath_hal *ah, const struct ieee80211_channel *chan,
65187831Ssam	HAL_CAL_TYPE calType)
66185380Ssam{
67185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
68185380Ssam
69185380Ssam	switch (calType & cal->suppCals) {
70185380Ssam	case IQ_MISMATCH_CAL:
71185380Ssam		/* Run IQ Mismatch for non-CCK only */
72187831Ssam		return !IEEE80211_IS_CHAN_B(chan);
73185380Ssam	case ADC_GAIN_CAL:
74185380Ssam	case ADC_DC_CAL:
75224510Sadrian		/*
76225421Sadrian		 * Run ADC Gain Cal for either 5ghz any or 2ghz HT40.
77225421Sadrian		 *
78225421Sadrian		 * Don't run ADC calibrations for 5ghz fast clock mode
79225421Sadrian		 * in HT20 - only one ADC is used.
80224510Sadrian		 */
81225421Sadrian		if (IEEE80211_IS_CHAN_HT20(chan) &&
82225421Sadrian		    (IS_5GHZ_FAST_CLOCK_EN(ah, chan)))
83225421Sadrian			return AH_FALSE;
84225421Sadrian		if (IEEE80211_IS_CHAN_5GHZ(chan))
85225421Sadrian			return AH_TRUE;
86219984Sadrian		if (IEEE80211_IS_CHAN_HT40(chan))
87219984Sadrian			return AH_TRUE;
88219984Sadrian		return AH_FALSE;
89185380Ssam	}
90185380Ssam	return AH_FALSE;
91185380Ssam}
92185380Ssam
93185380Ssam/*
94185380Ssam * Setup HW to collect samples used for current cal
95185380Ssam */
96185380Ssamstatic void
97185380Ssamar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
98185380Ssam{
99185380Ssam	/* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
100185380Ssam	OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
101185380Ssam	    AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
102185380Ssam	    currCal->calData->calCountMax);
103185380Ssam
104185380Ssam	/* Select calibration to run */
105185380Ssam	switch (currCal->calData->calType) {
106185380Ssam	case IQ_MISMATCH_CAL:
107185380Ssam		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
108185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
109185380Ssam		    "%s: start IQ Mismatch calibration\n", __func__);
110185380Ssam		break;
111185380Ssam	case ADC_GAIN_CAL:
112185380Ssam		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
113185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
114185380Ssam		    "%s: start ADC Gain calibration\n", __func__);
115185380Ssam		break;
116185380Ssam	case ADC_DC_CAL:
117185380Ssam		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
118185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
119185380Ssam		    "%s: start ADC DC calibration\n", __func__);
120185380Ssam		break;
121185380Ssam	case ADC_DC_INIT_CAL:
122185380Ssam		OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
123185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
124185380Ssam		    "%s: start Init ADC DC calibration\n", __func__);
125185380Ssam		break;
126185380Ssam	}
127185380Ssam	/* Kick-off cal */
128185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);
129185380Ssam}
130185380Ssam
131185380Ssam/*
132185380Ssam * Initialize shared data structures and prepare a cal to be run.
133185380Ssam */
134185380Ssamstatic void
135185380Ssamar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
136185380Ssam{
137185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
138185380Ssam
139185380Ssam	/* Reset data structures shared between different calibrations */
140185380Ssam	OS_MEMZERO(cal->caldata, sizeof(cal->caldata));
141185380Ssam	cal->calSamples = 0;
142185380Ssam
143185380Ssam	/* Setup HW for new calibration */
144185380Ssam	ar5416SetupMeasurement(ah, currCal);
145185380Ssam
146185380Ssam	/* Change SW state to RUNNING for this calibration */
147185380Ssam	currCal->calState = CAL_RUNNING;
148185380Ssam}
149185380Ssam
150185380Ssam#if 0
151185380Ssam/*
152185380Ssam * Run non-periodic calibrations.
153185380Ssam */
154185380Ssamstatic HAL_BOOL
155185380Ssamar5416RunInitCals(struct ath_hal *ah, int init_cal_count)
156185380Ssam{
157185380Ssam	struct ath_hal_5416 *ahp = AH5416(ah);
158185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
159185380Ssam	HAL_CHANNEL_INTERNAL ichan;	/* XXX bogus */
160185380Ssam	HAL_CAL_LIST *curCal = ahp->ah_cal_curr;
161185380Ssam	HAL_BOOL isCalDone;
162185380Ssam	int i;
163185380Ssam
164185380Ssam	if (curCal == AH_NULL)
165185380Ssam		return AH_FALSE;
166185380Ssam
167185380Ssam	ichan.calValid = 0;
168185380Ssam	for (i = 0; i < init_cal_count; i++) {
169185380Ssam		/* Reset this Cal */
170185380Ssam		ar5416ResetMeasurement(ah, curCal);
171185380Ssam		/* Poll for offset calibration complete */
172185380Ssam		if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
173185380Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
174185380Ssam			    "%s: Cal %d failed to finish in 100ms.\n",
175185380Ssam			    __func__, curCal->calData->calType);
176185380Ssam			/* Re-initialize list pointers for periodic cals */
177185380Ssam			cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
178185380Ssam			return AH_FALSE;
179185380Ssam		}
180185380Ssam		/* Run this cal */
181185380Ssam		ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,
182185380Ssam		    curCal, &isCalDone);
183185380Ssam		if (!isCalDone)
184185380Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
185185380Ssam			    "%s: init cal %d did not complete.\n",
186185380Ssam			    __func__, curCal->calData->calType);
187185380Ssam		if (curCal->calNext != AH_NULL)
188185380Ssam			curCal = curCal->calNext;
189185380Ssam	}
190185380Ssam
191185380Ssam	/* Re-initialize list pointers for periodic cals */
192185380Ssam	cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
193185380Ssam	return AH_TRUE;
194185380Ssam}
195185380Ssam#endif
196185380Ssam
197224509Sadrian
198224509Sadrian/*
199224509Sadrian * AGC calibration for the AR5416, AR9130, AR9160, AR9280.
200224509Sadrian */
201185380SsamHAL_BOOL
202219480Sadrianar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan)
203185380Ssam{
204224509Sadrian
205203882Srpaulo	if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
206224509Sadrian		/* Disable ADC */
207224509Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL,
208224509Sadrian		    AR_PHY_ADC_CTL_OFF_PWDADC);
209203159Srpaulo
210185380Ssam		/* Enable Rx Filter Cal */
211185380Ssam		OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
212185380Ssam		    AR_PHY_AGC_CONTROL_FLTR_CAL);
213185380Ssam	}
214185380Ssam
215185380Ssam	/* Calibrate the AGC */
216185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
217185380Ssam
218185380Ssam	/* Poll for offset calibration complete */
219203882Srpaulo	if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
220185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
221203882Srpaulo		    "%s: offset calibration did not complete in 1ms; "
222185380Ssam		    "noisy environment?\n", __func__);
223185380Ssam		return AH_FALSE;
224185380Ssam	}
225185380Ssam
226224509Sadrian	if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
227224509Sadrian		/* Enable ADC */
228224509Sadrian		OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL,
229224509Sadrian		    AR_PHY_ADC_CTL_OFF_PWDADC);
230224509Sadrian
231224509Sadrian		/* Disable Rx Filter Cal */
232224509Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
233224509Sadrian		    AR_PHY_AGC_CONTROL_FLTR_CAL);
234224509Sadrian	}
235224509Sadrian
236219480Sadrian	return AH_TRUE;
237219480Sadrian}
238219480Sadrian
239219480Sadrian/*
240219480Sadrian * Initialize Calibration infrastructure.
241219480Sadrian */
242219802Sadrian#define	MAX_CAL_CHECK		32
243219480SadrianHAL_BOOL
244219480Sadrianar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan)
245219480Sadrian{
246219480Sadrian	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
247219480Sadrian	HAL_CHANNEL_INTERNAL *ichan;
248219480Sadrian
249219480Sadrian	ichan = ath_hal_checkchannel(ah, chan);
250219480Sadrian	HALASSERT(ichan != AH_NULL);
251219480Sadrian
252219480Sadrian	/* Do initial chipset-specific calibration */
253219480Sadrian	if (! AH5416(ah)->ah_cal_initcal(ah, chan)) {
254224509Sadrian		HALDEBUG(ah, HAL_DEBUG_ANY,
255224509Sadrian		    "%s: initial chipset calibration did "
256219480Sadrian		    "not complete in time; noisy environment?\n", __func__);
257219480Sadrian		return AH_FALSE;
258219480Sadrian	}
259219480Sadrian
260219480Sadrian	/* If there's PA Cal, do it */
261219480Sadrian	if (AH5416(ah)->ah_cal_pacal)
262219480Sadrian		AH5416(ah)->ah_cal_pacal(ah, AH_TRUE);
263219480Sadrian
264185380Ssam	/*
265185380Ssam	 * Do NF calibration after DC offset and other CALs.
266185380Ssam	 * Per system engineers, noise floor value can sometimes be 20 dB
267185380Ssam	 * higher than normal value if DC offset and noise floor cal are
268185380Ssam	 * triggered at the same time.
269185380Ssam	 */
270185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
271219802Sadrian
272219802Sadrian	/*
273221773Sadrian	 * This may take a while to run; make sure subsequent
274221773Sadrian	 * calibration routines check that this has completed
275221773Sadrian	 * before reading the value and triggering a subsequent
276221773Sadrian	 * calibration.
277219802Sadrian	 */
278211330Sadrian
279185380Ssam	/* Initialize list pointers */
280185380Ssam	cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
281185380Ssam
282185380Ssam	/*
283185380Ssam	 * Enable IQ, ADC Gain, ADC DC Offset Cals
284185380Ssam	 */
285221868Sadrian	if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) {
286185380Ssam		/* Setup all non-periodic, init time only calibrations */
287185380Ssam		/* XXX: Init DC Offset not working yet */
288185380Ssam#if 0
289185380Ssam		if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
290185380Ssam			INIT_CAL(&cal->adcDcCalInitData);
291185380Ssam			INSERT_CAL(cal, &cal->adcDcCalInitData);
292185380Ssam		}
293185380Ssam		/* Initialize current pointer to first element in list */
294185380Ssam		cal->cal_curr = cal->cal_list;
295185380Ssam
296185380Ssam		if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
297185380Ssam			return AH_FALSE;
298185380Ssam#endif
299185380Ssam	}
300185380Ssam
301185380Ssam	/* If Cals are supported, add them to list via INIT/INSERT_CAL */
302185380Ssam	if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
303185380Ssam		INIT_CAL(&cal->adcGainCalData);
304185380Ssam		INSERT_CAL(cal, &cal->adcGainCalData);
305185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
306185380Ssam		    "%s: enable ADC Gain Calibration.\n", __func__);
307185380Ssam	}
308185380Ssam	if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
309185380Ssam		INIT_CAL(&cal->adcDcCalData);
310185380Ssam		INSERT_CAL(cal, &cal->adcDcCalData);
311185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
312185380Ssam		    "%s: enable ADC DC Calibration.\n", __func__);
313185380Ssam	}
314185380Ssam	if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
315185380Ssam		INIT_CAL(&cal->iqCalData);
316185380Ssam		INSERT_CAL(cal, &cal->iqCalData);
317185380Ssam		HALDEBUG(ah, HAL_DEBUG_PERCAL,
318185380Ssam		    "%s: enable IQ Calibration.\n", __func__);
319185380Ssam	}
320185380Ssam	/* Initialize current pointer to first element in list */
321185380Ssam	cal->cal_curr = cal->cal_list;
322185380Ssam
323185380Ssam	/* Kick off measurements for the first cal */
324185380Ssam	if (cal->cal_curr != AH_NULL)
325185380Ssam		ar5416ResetMeasurement(ah, cal->cal_curr);
326185380Ssam
327185380Ssam	/* Mark all calibrations on this channel as being invalid */
328185380Ssam	ichan->calValid = 0;
329185380Ssam
330185380Ssam	return AH_TRUE;
331219802Sadrian#undef	MAX_CAL_CHECK
332185380Ssam}
333185380Ssam
334185380Ssam/*
335185380Ssam * Entry point for upper layers to restart current cal.
336185380Ssam * Reset the calibration valid bit in channel.
337185380Ssam */
338185380SsamHAL_BOOL
339187831Ssamar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
340185380Ssam{
341185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
342185380Ssam	HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
343185380Ssam	HAL_CAL_LIST *currCal = cal->cal_curr;
344185380Ssam
345185380Ssam	if (!AR_SREV_SOWL_10_OR_LATER(ah))
346185380Ssam		return AH_FALSE;
347185380Ssam	if (currCal == AH_NULL)
348185380Ssam		return AH_FALSE;
349185380Ssam	if (ichan == AH_NULL) {
350185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
351185380Ssam		    "%s: invalid channel %u/0x%x; no mapping\n",
352187831Ssam		    __func__, chan->ic_freq, chan->ic_flags);
353185380Ssam		return AH_FALSE;
354185380Ssam	}
355185380Ssam	/*
356185380Ssam	 * Expected that this calibration has run before, post-reset.
357185380Ssam	 * Current state should be done
358185380Ssam	 */
359185380Ssam	if (currCal->calState != CAL_DONE) {
360185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
361185380Ssam		    "%s: Calibration state incorrect, %d\n",
362185380Ssam		    __func__, currCal->calState);
363185380Ssam		return AH_FALSE;
364185380Ssam	}
365185380Ssam
366185380Ssam	/* Verify Cal is supported on this channel */
367185380Ssam	if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
368185380Ssam		return AH_FALSE;
369185380Ssam
370185380Ssam	HALDEBUG(ah, HAL_DEBUG_PERCAL,
371185380Ssam	    "%s: Resetting Cal %d state for channel %u/0x%x\n",
372187831Ssam	    __func__, currCal->calData->calType, chan->ic_freq,
373187831Ssam	    chan->ic_flags);
374185380Ssam
375185380Ssam	/* Disable cal validity in channel */
376185380Ssam	ichan->calValid &= ~currCal->calData->calType;
377185380Ssam	currCal->calState = CAL_WAITING;
378185380Ssam
379185380Ssam	return AH_TRUE;
380185380Ssam}
381185380Ssam
382185380Ssam/*
383185380Ssam * Recalibrate the lower PHY chips to account for temperature/environment
384185380Ssam * changes.
385185380Ssam */
386185380Ssamstatic void
387185380Ssamar5416DoCalibration(struct ath_hal *ah,  HAL_CHANNEL_INTERNAL *ichan,
388185380Ssam	uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
389185380Ssam{
390185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
391185380Ssam
392185380Ssam	/* Cal is assumed not done until explicitly set below */
393185380Ssam	*isCalDone = AH_FALSE;
394185380Ssam
395185380Ssam	HALDEBUG(ah, HAL_DEBUG_PERCAL,
396185380Ssam	    "%s: %s Calibration, state %d, calValid 0x%x\n",
397185380Ssam	    __func__, currCal->calData->calName, currCal->calState,
398185380Ssam	    ichan->calValid);
399185380Ssam
400185380Ssam	/* Calibration in progress. */
401185380Ssam	if (currCal->calState == CAL_RUNNING) {
402185380Ssam		/* Check to see if it has finished. */
403185380Ssam		if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
404185380Ssam			HALDEBUG(ah, HAL_DEBUG_PERCAL,
405185380Ssam			    "%s: sample %d of %d finished\n",
406185380Ssam			    __func__, cal->calSamples,
407185380Ssam			    currCal->calData->calNumSamples);
408185380Ssam			/*
409185380Ssam			 * Collect measurements for active chains.
410185380Ssam			 */
411185380Ssam			currCal->calData->calCollect(ah);
412185380Ssam			if (++cal->calSamples >= currCal->calData->calNumSamples) {
413185380Ssam				int i, numChains = 0;
414185380Ssam				for (i = 0; i < AR5416_MAX_CHAINS; i++) {
415185380Ssam					if (rxchainmask & (1 << i))
416185380Ssam						numChains++;
417185380Ssam				}
418185380Ssam				/*
419185380Ssam				 * Process accumulated data
420185380Ssam				 */
421185380Ssam				currCal->calData->calPostProc(ah, numChains);
422185380Ssam
423185380Ssam				/* Calibration has finished. */
424185380Ssam				ichan->calValid |= currCal->calData->calType;
425185380Ssam				currCal->calState = CAL_DONE;
426185380Ssam				*isCalDone = AH_TRUE;
427185380Ssam			} else {
428185380Ssam				/*
429185380Ssam				 * Set-up to collect of another sub-sample.
430185380Ssam				 */
431185380Ssam				ar5416SetupMeasurement(ah, currCal);
432185380Ssam			}
433185380Ssam		}
434185380Ssam	} else if (!(ichan->calValid & currCal->calData->calType)) {
435185380Ssam		/* If current cal is marked invalid in channel, kick it off */
436185380Ssam		ar5416ResetMeasurement(ah, currCal);
437185380Ssam	}
438185380Ssam}
439185380Ssam
440185380Ssam/*
441185380Ssam * Internal interface to schedule periodic calibration work.
442185380Ssam */
443185380SsamHAL_BOOL
444187831Ssamar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan,
445185380Ssam	u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
446185380Ssam{
447185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
448185380Ssam	HAL_CAL_LIST *currCal = cal->cal_curr;
449185380Ssam	HAL_CHANNEL_INTERNAL *ichan;
450221779Sadrian	int r;
451185380Ssam
452188084Ssam	OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);
453185380Ssam
454185380Ssam	*isCalDone = AH_TRUE;
455185380Ssam
456211208Sadrian	/*
457211208Sadrian	 * Since ath_hal calls the PerCal method with rxchainmask=0x1;
458211208Sadrian	 * override it with the current chainmask. The upper levels currently
459211208Sadrian	 * doesn't know about the chainmask.
460211208Sadrian	 */
461211208Sadrian	rxchainmask = AH5416(ah)->ah_rx_chainmask;
462211208Sadrian
463185380Ssam	/* Invalid channel check */
464185380Ssam	ichan = ath_hal_checkchannel(ah, chan);
465185380Ssam	if (ichan == AH_NULL) {
466185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
467185380Ssam		    "%s: invalid channel %u/0x%x; no mapping\n",
468187831Ssam		    __func__, chan->ic_freq, chan->ic_flags);
469185380Ssam		return AH_FALSE;
470185380Ssam	}
471185380Ssam
472185380Ssam	/*
473185380Ssam	 * For given calibration:
474185380Ssam	 * 1. Call generic cal routine
475185380Ssam	 * 2. When this cal is done (isCalDone) if we have more cals waiting
476185380Ssam	 *    (eg after reset), mask this to upper layers by not propagating
477185380Ssam	 *    isCalDone if it is set to TRUE.
478185380Ssam	 *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
479185380Ssam	 *    to be run.
480185380Ssam	 */
481185380Ssam	if (currCal != AH_NULL &&
482185380Ssam	    (currCal->calState == CAL_RUNNING ||
483185380Ssam	     currCal->calState == CAL_WAITING)) {
484185380Ssam		ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
485185380Ssam		if (*isCalDone == AH_TRUE) {
486185380Ssam			cal->cal_curr = currCal = currCal->calNext;
487185380Ssam			if (currCal->calState == CAL_WAITING) {
488185380Ssam				*isCalDone = AH_FALSE;
489185380Ssam				ar5416ResetMeasurement(ah, currCal);
490185380Ssam			}
491185380Ssam		}
492185380Ssam	}
493185380Ssam
494185380Ssam	/* Do NF cal only at longer intervals */
495185380Ssam	if (longcal) {
496219480Sadrian		/* Do PA calibration if the chipset supports */
497219480Sadrian		if (AH5416(ah)->ah_cal_pacal)
498219480Sadrian			AH5416(ah)->ah_cal_pacal(ah, AH_FALSE);
499219480Sadrian
500221837Sadrian		/* Do open-loop temperature compensation if the chipset needs it */
501221837Sadrian		if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
502221837Sadrian			AH5416(ah)->ah_olcTempCompensation(ah);
503219393Sadrian
504185380Ssam		/*
505185380Ssam		 * Get the value from the previous NF cal
506185380Ssam		 * and update the history buffer.
507185380Ssam		 */
508221779Sadrian		r = ar5416GetNf(ah, chan);
509221944Sadrian		if (r == 0 || r == -1) {
510221779Sadrian			/* NF calibration result isn't valid */
511221779Sadrian			HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration"
512221779Sadrian			    " didn't finish; delaying CCA\n", __func__);
513221779Sadrian		} else {
514221779Sadrian			/*
515221779Sadrian			 * NF calibration result is valid.
516221779Sadrian			 *
517221779Sadrian			 * Load the NF from history buffer of the current channel.
518221779Sadrian			 * NF is slow time-variant, so it is OK to use a
519221779Sadrian			 * historical value.
520221779Sadrian			 */
521221779Sadrian			ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
522185380Ssam
523221779Sadrian			/* start NF calibration, without updating BB NF register*/
524221779Sadrian			ar5416StartNFCal(ah);
525221779Sadrian		}
526185380Ssam	}
527185380Ssam	return AH_TRUE;
528185380Ssam}
529185380Ssam
530185380Ssam/*
531185380Ssam * Recalibrate the lower PHY chips to account for temperature/environment
532185380Ssam * changes.
533185380Ssam */
534185380SsamHAL_BOOL
535187831Ssamar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
536187831Ssam	HAL_BOOL *isIQdone)
537185380Ssam{
538185380Ssam	struct ath_hal_5416 *ahp = AH5416(ah);
539185380Ssam	struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
540185380Ssam	HAL_CAL_LIST *curCal = cal->cal_curr;
541185380Ssam
542185380Ssam	if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
543185380Ssam		return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
544185380Ssam		    AH_TRUE, isIQdone);
545185380Ssam	} else {
546185380Ssam		HAL_BOOL isCalDone;
547185380Ssam
548185380Ssam		*isIQdone = AH_FALSE;
549185380Ssam		return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
550185380Ssam		    AH_TRUE, &isCalDone);
551185380Ssam	}
552185380Ssam}
553185380Ssam
554185380Ssamstatic HAL_BOOL
555185380Ssamar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
556187831Ssam	const struct ieee80211_channel *chan, int16_t *nft)
557185380Ssam{
558187831Ssam	if (IEEE80211_IS_CHAN_5GHZ(chan)) {
559185380Ssam		ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
560187831Ssam		return AH_TRUE;
561187831Ssam	}
562187831Ssam	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
563185380Ssam		ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
564187831Ssam		return AH_TRUE;
565185380Ssam	}
566187831Ssam	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
567187831Ssam	    __func__, chan->ic_flags);
568187831Ssam	return AH_FALSE;
569185380Ssam}
570185380Ssam
571185380Ssamstatic void
572185380Ssamar5416StartNFCal(struct ath_hal *ah)
573185380Ssam{
574185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
575185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
576185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
577185380Ssam}
578185380Ssam
579185380Ssamstatic void
580187831Ssamar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
581185380Ssam{
582185380Ssam	static const uint32_t ar5416_cca_regs[] = {
583185380Ssam		AR_PHY_CCA,
584185380Ssam		AR_PHY_CH1_CCA,
585185380Ssam		AR_PHY_CH2_CCA,
586185380Ssam		AR_PHY_EXT_CCA,
587185380Ssam		AR_PHY_CH1_EXT_CCA,
588185380Ssam		AR_PHY_CH2_EXT_CCA
589185380Ssam	};
590185380Ssam	struct ar5212NfCalHist *h;
591211211Sadrian	int i;
592185380Ssam	int32_t val;
593185380Ssam	uint8_t chainmask;
594218068Sadrian	int16_t default_nf = ar5416GetDefaultNF(ah, chan);
595185380Ssam
596185380Ssam	/*
597185380Ssam	 * Force NF calibration for all chains.
598185380Ssam	 */
599185380Ssam	if (AR_SREV_KITE(ah)) {
600185380Ssam		/* Kite has only one chain */
601185380Ssam		chainmask = 0x9;
602222315Sadrian	} else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) {
603222315Sadrian		/* Merlin/Kiwi has only two chains */
604185380Ssam		chainmask = 0x1B;
605185380Ssam	} else {
606185380Ssam		chainmask = 0x3F;
607185380Ssam	}
608185380Ssam
609185380Ssam	/*
610185380Ssam	 * Write filtered NF values into maxCCApwr register parameter
611185380Ssam	 * so we can load below.
612185380Ssam	 */
613185380Ssam	h = AH5416(ah)->ah_cal.nfCalHist;
614218068Sadrian	HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: ");
615218068Sadrian	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
616218069Sadrian
617221488Sadrian		/* Don't write to EXT radio CCA registers unless in HT/40 mode */
618218069Sadrian		/* XXX this check should really be cleaner! */
619221488Sadrian		if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
620218069Sadrian			continue;
621218069Sadrian
622185380Ssam		if (chainmask & (1 << i)) {
623218068Sadrian			int16_t nf_val;
624218068Sadrian
625218068Sadrian			if (h)
626218068Sadrian				nf_val = h[i].privNF;
627218068Sadrian			else
628218068Sadrian				nf_val = default_nf;
629218068Sadrian
630185380Ssam			val = OS_REG_READ(ah, ar5416_cca_regs[i]);
631185380Ssam			val &= 0xFFFFFE00;
632218068Sadrian			val |= (((uint32_t) nf_val << 1) & 0x1ff);
633218068Sadrian			HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val);
634185380Ssam			OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
635185380Ssam		}
636218068Sadrian	}
637218068Sadrian	HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n");
638185380Ssam
639185380Ssam	/* Load software filtered NF value into baseband internal minCCApwr variable. */
640185380Ssam	OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
641185380Ssam	OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
642185380Ssam	OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
643185380Ssam
644211214Sadrian	/* Wait for load to complete, should be fast, a few 10s of us. */
645227411Sadrian	if (! ar5212WaitNFCalComplete(ah, 1000)) {
646211211Sadrian		/*
647211211Sadrian		 * We timed out waiting for the noisefloor to load, probably due to an
648211211Sadrian		 * in-progress rx. Simply return here and allow the load plenty of time
649211211Sadrian		 * to complete before the next calibration interval.  We need to avoid
650211211Sadrian		 * trying to load -50 (which happens below) while the previous load is
651211211Sadrian		 * still in progress as this can cause rx deafness. Instead by returning
652211211Sadrian		 * here, the baseband nf cal will just be capped by our present
653211211Sadrian		 * noisefloor until the next calibration timer.
654211211Sadrian		 */
655221778Sadrian		HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for "
656221778Sadrian		    "nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
657211211Sadrian		    OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
658211214Sadrian		return;
659185380Ssam	}
660185380Ssam
661185380Ssam	/*
662185380Ssam	 * Restore maxCCAPower register parameter again so that we're not capped
663185380Ssam	 * by the median we just loaded.  This will be initial (and max) value
664185380Ssam	 * of next noise floor calibration the baseband does.
665185380Ssam	 */
666277290Sadrian	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
667221488Sadrian
668221488Sadrian		/* Don't write to EXT radio CCA registers unless in HT/40 mode */
669221488Sadrian		/* XXX this check should really be cleaner! */
670221488Sadrian		if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
671221488Sadrian			continue;
672221488Sadrian
673185380Ssam		if (chainmask & (1 << i)) {
674185380Ssam			val = OS_REG_READ(ah, ar5416_cca_regs[i]);
675185380Ssam			val &= 0xFFFFFE00;
676185380Ssam			val |= (((uint32_t)(-50) << 1) & 0x1ff);
677185380Ssam			OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
678185380Ssam		}
679277290Sadrian	}
680185380Ssam}
681185380Ssam
682218068Sadrian/*
683218068Sadrian * This just initialises the "good" values for AR5416 which
684218068Sadrian * may not be right; it'lll be overridden by ar5416SanitizeNF()
685218068Sadrian * to nominal values.
686218068Sadrian */
687185380Ssamvoid
688203882Srpauloar5416InitNfHistBuff(struct ar5212NfCalHist *h)
689185380Ssam{
690185380Ssam	int i, j;
691185380Ssam
692185380Ssam	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
693185380Ssam		h[i].currIndex = 0;
694185380Ssam		h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
695185380Ssam		h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
696185380Ssam		for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
697185380Ssam			h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
698185380Ssam	}
699185380Ssam}
700185380Ssam
701185380Ssam/*
702185380Ssam * Update the noise floor buffer as a ring buffer
703185380Ssam */
704185380Ssamstatic void
705221488Sadrianar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h,
706221488Sadrian    int16_t *nfarray)
707185380Ssam{
708185380Ssam	int i;
709185380Ssam
710221488Sadrian	/* XXX TODO: don't record nfarray[] entries for inactive chains */
711185380Ssam	for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
712185380Ssam		h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
713185380Ssam
714185380Ssam		if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
715185380Ssam			h[i].currIndex = 0;
716185380Ssam		if (h[i].invalidNFcount > 0) {
717185380Ssam			if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
718185380Ssam			    nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
719185380Ssam				h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
720185380Ssam			} else {
721185380Ssam				h[i].invalidNFcount--;
722185380Ssam				h[i].privNF = nfarray[i];
723185380Ssam			}
724185380Ssam		} else {
725185380Ssam			h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
726185380Ssam		}
727185380Ssam	}
728185380Ssam}
729185380Ssam
730218068Sadrianstatic uint16_t
731218068Sadrianar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
732218068Sadrian{
733218068Sadrian        struct ar5416NfLimits *limit;
734218068Sadrian
735218068Sadrian        if (!chan || IEEE80211_IS_CHAN_2GHZ(chan))
736218068Sadrian                limit = &AH5416(ah)->nf_2g;
737218068Sadrian        else
738218068Sadrian                limit = &AH5416(ah)->nf_5g;
739218068Sadrian
740218068Sadrian        return limit->nominal;
741218068Sadrian}
742218068Sadrian
743218068Sadrianstatic void
744218068Sadrianar5416SanitizeNF(struct ath_hal *ah, int16_t *nf)
745218068Sadrian{
746218068Sadrian
747218068Sadrian        struct ar5416NfLimits *limit;
748218068Sadrian        int i;
749218068Sadrian
750218068Sadrian        if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan))
751218068Sadrian                limit = &AH5416(ah)->nf_2g;
752218068Sadrian        else
753218068Sadrian                limit = &AH5416(ah)->nf_5g;
754218068Sadrian
755218068Sadrian        for (i = 0; i < AR5416_NUM_NF_READINGS; i++) {
756218068Sadrian                if (!nf[i])
757218068Sadrian                        continue;
758218068Sadrian
759218068Sadrian                if (nf[i] > limit->max) {
760218068Sadrian                        HALDEBUG(ah, HAL_DEBUG_NFCAL,
761218068Sadrian                                  "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
762218068Sadrian                                  i, nf[i], limit->max);
763218068Sadrian                        nf[i] = limit->max;
764218068Sadrian                } else if (nf[i] < limit->min) {
765218068Sadrian                        HALDEBUG(ah, HAL_DEBUG_NFCAL,
766218068Sadrian                                  "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
767218068Sadrian                                  i, nf[i], limit->min);
768218068Sadrian                        nf[i] = limit->nominal;
769218068Sadrian                }
770218068Sadrian        }
771218068Sadrian}
772218068Sadrian
773218068Sadrian
774185380Ssam/*
775298939Spfg * Read the NF and check it against the noise floor threshold
776221779Sadrian *
777221779Sadrian * Return 0 if the NF calibration hadn't finished, 0 if it was
778221779Sadrian * invalid, or > 0 for a valid NF reading.
779185380Ssam */
780185380Ssamstatic int16_t
781187831Ssamar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)
782185380Ssam{
783185380Ssam	int16_t nf, nfThresh;
784220442Sadrian	int i;
785221779Sadrian	int retval = 0;
786185380Ssam
787211210Sadrian	if (ar5212IsNFCalInProgress(ah)) {
788185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
789185380Ssam		    "%s: NF didn't complete in calibration window\n", __func__);
790185380Ssam		nf = 0;
791221779Sadrian		retval = -1;	/* NF didn't finish */
792185380Ssam	} else {
793185380Ssam		/* Finished NF cal, check against threshold */
794185380Ssam		int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
795187831Ssam		HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
796185380Ssam
797185380Ssam		/* TODO - enhance for multiple chains and ext ch */
798185380Ssam		ath_hal_getNoiseFloor(ah, nfarray);
799185380Ssam		nf = nfarray[0];
800218068Sadrian		ar5416SanitizeNF(ah, nfarray);
801185380Ssam		if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
802185380Ssam			if (nf > nfThresh) {
803221779Sadrian				HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
804185380Ssam				    "%s: noise floor failed detected; "
805185380Ssam				    "detected %d, threshold %d\n", __func__,
806185380Ssam				    nf, nfThresh);
807185380Ssam				/*
808185380Ssam				 * NB: Don't discriminate 2.4 vs 5Ghz, if this
809185380Ssam				 *     happens it indicates a problem regardless
810185380Ssam				 *     of the band.
811185380Ssam				 */
812187831Ssam				chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
813185380Ssam				nf = 0;
814221779Sadrian				retval = 0;
815185380Ssam			}
816185380Ssam		} else {
817185380Ssam			nf = 0;
818221779Sadrian			retval = 0;
819185380Ssam		}
820220442Sadrian		/* Update MIMO channel statistics, regardless of validity or not (for now) */
821220442Sadrian		for (i = 0; i < 3; i++) {
822220442Sadrian			ichan->noiseFloorCtl[i] = nfarray[i];
823220442Sadrian			ichan->noiseFloorExt[i] = nfarray[i + 3];
824220442Sadrian		}
825220442Sadrian		ichan->privFlags |= CHANNEL_MIMO_NF_VALID;
826220442Sadrian
827221488Sadrian		ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray);
828187831Ssam		ichan->rawNoiseFloor = nf;
829221779Sadrian		retval = nf;
830185380Ssam	}
831221779Sadrian	return retval;
832185380Ssam}
833