ar5212_misc.c revision 222821
1327952Sdim/*
2212793Sdim * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3353358Sdim * Copyright (c) 2002-2008 Atheros Communications, Inc.
4353358Sdim *
5353358Sdim * Permission to use, copy, modify, and/or distribute this software for any
6212793Sdim * purpose with or without fee is hereby granted, provided that the above
7212793Sdim * copyright notice and this permission notice appear in all copies.
8212793Sdim *
9212793Sdim * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10212793Sdim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11212793Sdim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12212793Sdim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13212793Sdim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14212793Sdim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15212793Sdim * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16321369Sdim *
17276479Sdim * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c 222821 2011-06-07 14:00:47Z adrian $
18218893Sdim */
19249423Sdim#include "opt_ah.h"
20276479Sdim
21276479Sdim#include "ah.h"
22249423Sdim#include "ah_internal.h"
23226633Sdim#include "ah_devid.h"
24321369Sdim#include "ah_desc.h"			/* NB: for HAL_PHYERR* */
25327952Sdim
26321369Sdim#include "ar5212/ar5212.h"
27321369Sdim#include "ar5212/ar5212reg.h"
28321369Sdim#include "ar5212/ar5212phy.h"
29321369Sdim
30321369Sdim#include "ah_eeprom_v3.h"
31212793Sdim
32212793Sdim#define	AR_NUM_GPIO	6		/* 6 GPIO pins */
33321369Sdim#define	AR_GPIOD_MASK	0x0000002F	/* GPIO data reg r/w mask */
34327952Sdim
35212793Sdimvoid
36212793Sdimar5212GetMacAddress(struct ath_hal *ah, uint8_t *mac)
37327952Sdim{
38321369Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
39249423Sdim
40218893Sdim	OS_MEMCPY(mac, ahp->ah_macaddr, IEEE80211_ADDR_LEN);
41321369Sdim}
42212793Sdim
43321369SdimHAL_BOOL
44327952Sdimar5212SetMacAddress(struct ath_hal *ah, const uint8_t *mac)
45327952Sdim{
46327952Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
47327952Sdim
48321369Sdim	OS_MEMCPY(ahp->ah_macaddr, mac, IEEE80211_ADDR_LEN);
49321369Sdim	return AH_TRUE;
50321369Sdim}
51276479Sdim
52249423Sdimvoid
53321369Sdimar5212GetBssIdMask(struct ath_hal *ah, uint8_t *mask)
54321369Sdim{
55249423Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
56249423Sdim
57321369Sdim	OS_MEMCPY(mask, ahp->ah_bssidmask, IEEE80211_ADDR_LEN);
58276479Sdim}
59321369Sdim
60249423SdimHAL_BOOL
61321369Sdimar5212SetBssIdMask(struct ath_hal *ah, const uint8_t *mask)
62321369Sdim{
63249423Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
64249423Sdim
65327952Sdim	/* save it since it must be rewritten on reset */
66249423Sdim	OS_MEMCPY(ahp->ah_bssidmask, mask, IEEE80211_ADDR_LEN);
67249423Sdim
68321369Sdim	OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask));
69321369Sdim	OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4));
70321369Sdim	return AH_TRUE;
71321369Sdim}
72321369Sdim
73321369Sdim/*
74321369Sdim * Attempt to change the cards operating regulatory domain to the given value
75212793Sdim */
76341825SdimHAL_BOOL
77321369Sdimar5212SetRegulatoryDomain(struct ath_hal *ah,
78212793Sdim	uint16_t regDomain, HAL_STATUS *status)
79212793Sdim{
80321369Sdim	HAL_STATUS ecode;
81321369Sdim
82321369Sdim	if (AH_PRIVATE(ah)->ah_currentRD == regDomain) {
83321369Sdim		ecode = HAL_EINVAL;
84212793Sdim		goto bad;
85212793Sdim	}
86212793Sdim	if (ath_hal_eepromGetFlag(ah, AR_EEP_WRITEPROTECT)) {
87212793Sdim		ecode = HAL_EEWRITE;
88218893Sdim		goto bad;
89327952Sdim	}
90218893Sdim#ifdef AH_SUPPORT_WRITE_REGDOMAIN
91218893Sdim	if (ath_hal_eepromWrite(ah, AR_EEPROM_REG_DOMAIN, regDomain)) {
92218893Sdim		HALDEBUG(ah, HAL_DEBUG_ANY,
93321369Sdim		    "%s: set regulatory domain to %u (0x%x)\n",
94218893Sdim		    __func__, regDomain, regDomain);
95218893Sdim		AH_PRIVATE(ah)->ah_currentRD = regDomain;
96218893Sdim		return AH_TRUE;
97218893Sdim	}
98218893Sdim#endif
99218893Sdim	ecode = HAL_EIO;
100321369Sdimbad:
101218893Sdim	if (status)
102218893Sdim		*status = ecode;
103321369Sdim	return AH_FALSE;
104321369Sdim}
105321369Sdim
106327952Sdim/*
107218893Sdim * Return the wireless modes (a,b,g,t) supported by hardware.
108276479Sdim *
109212793Sdim * This value is what is actually supported by the hardware
110212793Sdim * and is unaffected by regulatory/country code settings.
111212793Sdim */
112276479Sdimu_int
113212793Sdimar5212GetWirelessModes(struct ath_hal *ah)
114212793Sdim{
115212793Sdim	u_int mode = 0;
116218893Sdim
117212793Sdim	if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) {
118218893Sdim		mode = HAL_MODE_11A;
119234353Sdim		if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE))
120218893Sdim			mode |= HAL_MODE_TURBO | HAL_MODE_108A;
121212793Sdim		if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate)
122212793Sdim			mode |= HAL_MODE_11A_HALF_RATE;
123239462Sdim		if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate)
124239462Sdim			mode |= HAL_MODE_11A_QUARTER_RATE;
125280031Sdim	}
126288943Sdim	if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE))
127288943Sdim		mode |= HAL_MODE_11B;
128280031Sdim	if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) &&
129288943Sdim	    AH_PRIVATE(ah)->ah_subvendorid != AR_SUBVENDOR_ID_NOG) {
130288943Sdim		mode |= HAL_MODE_11G;
131212793Sdim		if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO2DISABLE))
132234353Sdim			mode |= HAL_MODE_108G;
133218893Sdim		if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate)
134212793Sdim			mode |= HAL_MODE_11G_HALF_RATE;
135212793Sdim		if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate)
136321369Sdim			mode |= HAL_MODE_11G_QUARTER_RATE;
137212793Sdim	}
138321369Sdim	return mode;
139280031Sdim}
140243830Sdim
141243830Sdim/*
142280031Sdim * Set the interrupt and GPIO values so the ISR can disable RF
143243830Sdim * on a switch signal.  Assumes GPIO port and interrupt polarity
144243830Sdim * are set prior to call.
145243830Sdim */
146280031Sdimvoid
147243830Sdimar5212EnableRfKill(struct ath_hal *ah)
148243830Sdim{
149243830Sdim	uint16_t rfsilent = AH_PRIVATE(ah)->ah_rfsilent;
150280031Sdim	int select = MS(rfsilent, AR_EEPROM_RFSILENT_GPIO_SEL);
151243830Sdim	int polarity = MS(rfsilent, AR_EEPROM_RFSILENT_POLARITY);
152243830Sdim
153221345Sdim	/*
154212793Sdim	 * Configure the desired GPIO port for input
155321369Sdim	 * and enable baseband rf silence.
156280031Sdim	 */
157280031Sdim	ath_hal_gpioCfgInput(ah, select);
158280031Sdim	OS_REG_SET_BIT(ah, AR_PHY(0), 0x00002000);
159276479Sdim	/*
160276479Sdim	 * If radio disable switch connection to GPIO bit x is enabled
161280031Sdim	 * program GPIO interrupt.
162321369Sdim	 * If rfkill bit on eeprom is 1, setupeeprommap routine has already
163212793Sdim	 * verified that it is a later version of eeprom, it has a place for
164218893Sdim	 * rfkill bit and it is set to 1, indicating that GPIO bit x hardware
165212793Sdim	 * connection is present.
166321369Sdim	 */
167218893Sdim	ath_hal_gpioSetIntr(ah, select,
168218893Sdim	    (ath_hal_gpioGet(ah, select) == polarity ? !polarity : polarity));
169218893Sdim}
170234353Sdim
171218893Sdim/*
172218893Sdim * Change the LED blinking pattern to correspond to the connectivity
173218893Sdim */
174234353Sdimvoid
175234353Sdimar5212SetLedState(struct ath_hal *ah, HAL_LED_STATE state)
176234353Sdim{
177234353Sdim	static const uint32_t ledbits[8] = {
178234353Sdim		AR_PCICFG_LEDCTL_NONE,	/* HAL_LED_INIT */
179234353Sdim		AR_PCICFG_LEDCTL_PEND,	/* HAL_LED_SCAN */
180234353Sdim		AR_PCICFG_LEDCTL_PEND,	/* HAL_LED_AUTH */
181234353Sdim		AR_PCICFG_LEDCTL_ASSOC,	/* HAL_LED_ASSOC*/
182218893Sdim		AR_PCICFG_LEDCTL_ASSOC,	/* HAL_LED_RUN */
183218893Sdim		AR_PCICFG_LEDCTL_NONE,
184234353Sdim		AR_PCICFG_LEDCTL_NONE,
185234353Sdim		AR_PCICFG_LEDCTL_NONE,
186239462Sdim	};
187212793Sdim	uint32_t bits;
188212793Sdim
189321369Sdim	bits = OS_REG_READ(ah, AR_PCICFG);
190309124Sdim	if (IS_2417(ah)) {
191226633Sdim		/*
192226633Sdim		 * Enable LED for Nala. There is a bit marked reserved
193234353Sdim		 * that must be set and we also turn on the power led.
194360784Sdim		 * Because we mark s/w LED control setting the control
195360784Sdim		 * status bits below is meangless (the driver must flash
196234353Sdim		 * the LED(s) using the GPIO lines).
197234353Sdim		 */
198249423Sdim		bits = (bits &~ AR_PCICFG_LEDMODE)
199234353Sdim		     | SM(AR_PCICFG_LEDMODE_POWON, AR_PCICFG_LEDMODE)
200218893Sdim#if 0
201249423Sdim		     | SM(AR_PCICFG_LEDMODE_NETON, AR_PCICFG_LEDMODE)
202234353Sdim#endif
203249423Sdim		     | 0x08000000;
204249423Sdim	}
205249423Sdim	bits = (bits &~ AR_PCICFG_LEDCTL)
206249423Sdim	     | SM(ledbits[state & 0x7], AR_PCICFG_LEDCTL);
207249423Sdim	OS_REG_WRITE(ah, AR_PCICFG, bits);
208249423Sdim}
209249423Sdim
210249423Sdim/*
211239462Sdim * Change association related fields programmed into the hardware.
212249423Sdim * Writing a valid BSSID to the hardware effectively enables the hardware
213218893Sdim * to synchronize its TSF to the correct beacons and receive frames coming
214288943Sdim * from that BSSID. It is called by the SME JOIN operation.
215276479Sdim */
216218893Sdimvoid
217321369Sdimar5212WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId)
218239462Sdim{
219239462Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
220239462Sdim
221218893Sdim	/* XXX save bssid for possible re-use on reset */
222360784Sdim	OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN);
223218893Sdim	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
224218893Sdim	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) |
225360784Sdim				     ((assocId & 0x3fff)<<AR_BSS_ID1_AID_S));
226218893Sdim}
227239462Sdim
228239462Sdim/*
229239462Sdim * Get the current hardware tsf for stamlme
230360784Sdim */
231218893Sdimuint64_t
232239462Sdimar5212GetTsf64(struct ath_hal *ah)
233218893Sdim{
234218893Sdim	uint32_t low1, low2, u32;
235218893Sdim
236321369Sdim	/* sync multi-word read */
237221345Sdim	low1 = OS_REG_READ(ah, AR_TSF_L32);
238212793Sdim	u32 = OS_REG_READ(ah, AR_TSF_U32);
239212793Sdim	low2 = OS_REG_READ(ah, AR_TSF_L32);
240249423Sdim	if (low2 < low1) {	/* roll over */
241223017Sdim		/*
242309124Sdim		 * If we are not preempted this will work.  If we are
243212793Sdim		 * then we re-reading AR_TSF_U32 does no good as the
244212793Sdim		 * low bits will be meaningless.  Likewise reading
245212793Sdim		 * L32, U32, U32, then comparing the last two reads
246212793Sdim		 * to check for rollover doesn't help if preempted--so
247212793Sdim		 * we take this approach as it costs one less PCI read
248212793Sdim		 * which can be noticeable when doing things like
249212793Sdim		 * timestamping packets in monitor mode.
250212793Sdim		 */
251234353Sdim		u32++;
252212793Sdim	}
253212793Sdim	return (((uint64_t) u32) << 32) | ((uint64_t) low2);
254212793Sdim}
255321369Sdim
256212793Sdim/*
257212793Sdim * Get the current hardware tsf for stamlme
258212793Sdim */
259212793Sdimuint32_t
260212793Sdimar5212GetTsf32(struct ath_hal *ah)
261212793Sdim{
262212793Sdim	return OS_REG_READ(ah, AR_TSF_L32);
263221345Sdim}
264224145Sdim
265221345Sdimvoid
266261991Sdimar5212SetTsf64(struct ath_hal *ah, uint64_t tsf64)
267224145Sdim{
268221345Sdim	OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff);
269261991Sdim	OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff);
270221345Sdim}
271321369Sdim
272321369Sdim/*
273221345Sdim * Reset the current hardware tsf for stamlme.
274221345Sdim */
275221345Sdimvoid
276221345Sdimar5212ResetTsf(struct ath_hal *ah)
277221345Sdim{
278212793Sdim
279212793Sdim	uint32_t val = OS_REG_READ(ah, AR_BEACON);
280218893Sdim
281218893Sdim	OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF);
282218893Sdim	/*
283212793Sdim	 * When resetting the TSF, write twice to the
284212793Sdim	 * corresponding register; each write to the RESET_TSF bit toggles
285212793Sdim	 * the internal signal to cause a reset of the TSF - but if the signal
286212793Sdim	 * is left high, it will reset the TSF on the next chip reset also!
287221345Sdim	 * writing the bit an even number of times fixes this issue
288221345Sdim	 */
289221345Sdim	OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF);
290261991Sdim}
291321369Sdim
292218893Sdim/*
293212793Sdim * Set or clear hardware basic rate bit
294212793Sdim * Set hardware basic rate set if basic rate is found
295212793Sdim * and basic rate is equal or less than 2Mbps
296321369Sdim */
297321369Sdimvoid
298212793Sdimar5212SetBasicRate(struct ath_hal *ah, HAL_RATE_SET *rs)
299212793Sdim{
300212793Sdim	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
301280031Sdim	uint32_t reg;
302212793Sdim	uint8_t xset;
303212793Sdim	int i;
304360784Sdim
305224145Sdim	if (chan == AH_NULL || !IEEE80211_IS_CHAN_CCK(chan))
306212793Sdim		return;
307261991Sdim	xset = 0;
308261991Sdim	for (i = 0; i < rs->rs_count; i++) {
309261991Sdim		uint8_t rset = rs->rs_rates[i];
310234353Sdim		/* Basic rate defined? */
311276479Sdim		if ((rset & 0x80) && (rset &= 0x7f) >= xset)
312276479Sdim			xset = rset;
313234353Sdim	}
314276479Sdim	/*
315212793Sdim	 * Set the h/w bit to reflect whether or not the basic
316276479Sdim	 * rate is found to be equal or less than 2Mbps.
317212793Sdim	 */
318212793Sdim	reg = OS_REG_READ(ah, AR_STA_ID1);
319212793Sdim	if (xset && xset/2 <= 2)
320212793Sdim		OS_REG_WRITE(ah, AR_STA_ID1, reg | AR_STA_ID1_BASE_RATE_11B);
321212793Sdim	else
322212793Sdim		OS_REG_WRITE(ah, AR_STA_ID1, reg &~ AR_STA_ID1_BASE_RATE_11B);
323280031Sdim}
324212793Sdim
325212793Sdim/*
326212793Sdim * Grab a semi-random value from hardware registers - may not
327212793Sdim * change often
328224145Sdim */
329212793Sdimuint32_t
330261991Sdimar5212GetRandomSeed(struct ath_hal *ah)
331261991Sdim{
332261991Sdim	uint32_t nf;
333261991Sdim
334261991Sdim	nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff;
335234353Sdim	if (nf & 0x100)
336276479Sdim		nf = 0 - ((nf ^ 0x1ff) + 1);
337276479Sdim	return (OS_REG_READ(ah, AR_TSF_U32) ^
338276479Sdim		OS_REG_READ(ah, AR_TSF_L32) ^ nf);
339276479Sdim}
340234353Sdim
341276479Sdim/*
342212793Sdim * Detect if our card is present
343212793Sdim */
344276479SdimHAL_BOOL
345212793Sdimar5212DetectCardPresent(struct ath_hal *ah)
346212793Sdim{
347212793Sdim	uint16_t macVersion, macRev;
348212793Sdim	uint32_t v;
349212793Sdim
350212793Sdim	/*
351280031Sdim	 * Read the Silicon Revision register and compare that
352212793Sdim	 * to what we read at attach time.  If the same, we say
353212793Sdim	 * a card/device is present.
354212793Sdim	 */
355212793Sdim	v = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID;
356224145Sdim	macVersion = v >> AR_SREV_ID_S;
357212793Sdim	macRev = v & AR_SREV_REVISION;
358261991Sdim	return (AH_PRIVATE(ah)->ah_macVersion == macVersion &&
359261991Sdim		AH_PRIVATE(ah)->ah_macRev == macRev);
360261991Sdim}
361234353Sdim
362276479Sdimvoid
363276479Sdimar5212EnableMibCounters(struct ath_hal *ah)
364276479Sdim{
365276479Sdim	/* NB: this just resets the mib counter machinery */
366234353Sdim	OS_REG_WRITE(ah, AR_MIBC,
367276479Sdim	    ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);
368212793Sdim}
369212793Sdim
370276479Sdimvoid
371212793Sdimar5212DisableMibCounters(struct ath_hal *ah)
372212793Sdim{
373212793Sdim	OS_REG_WRITE(ah, AR_MIBC,  AR_MIBC | AR_MIBC_CMC);
374212793Sdim}
375212793Sdim
376212793Sdim/*
377280031Sdim * Update MIB Counters
378212793Sdim */
379212793Sdimvoid
380212793Sdimar5212UpdateMibCounters(struct ath_hal *ah, HAL_MIB_STATS* stats)
381224145Sdim{
382218893Sdim	stats->ackrcv_bad += OS_REG_READ(ah, AR_ACK_FAIL);
383234353Sdim	stats->rts_bad	  += OS_REG_READ(ah, AR_RTS_FAIL);
384276479Sdim	stats->fcs_bad	  += OS_REG_READ(ah, AR_FCS_FAIL);
385276479Sdim	stats->rts_good	  += OS_REG_READ(ah, AR_RTS_OK);
386234353Sdim	stats->beacons	  += OS_REG_READ(ah, AR_BEACON_CNT);
387276479Sdim}
388212793Sdim
389276479Sdim/*
390212793Sdim * Detect if the HW supports spreading a CCK signal on channel 14
391212793Sdim */
392212793SdimHAL_BOOL
393212793Sdimar5212IsJapanChannelSpreadSupported(struct ath_hal *ah)
394212793Sdim{
395212793Sdim	return AH_TRUE;
396218893Sdim}
397218893Sdim
398249423Sdim/*
399218893Sdim * Get the rssi of frame curently being received.
400212793Sdim */
401218893Sdimuint32_t
402276479Sdimar5212GetCurRssi(struct ath_hal *ah)
403234353Sdim{
404218893Sdim	return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff);
405218893Sdim}
406218893Sdim
407212793Sdimu_int
408249423Sdimar5212GetDefAntenna(struct ath_hal *ah)
409218893Sdim{
410218893Sdim	return (OS_REG_READ(ah, AR_DEF_ANTENNA) & 0x7);
411218893Sdim}
412276479Sdim
413234353Sdimvoid
414218893Sdimar5212SetDefAntenna(struct ath_hal *ah, u_int antenna)
415218893Sdim{
416218893Sdim	OS_REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
417218893Sdim}
418218893Sdim
419218893SdimHAL_ANT_SETTING
420218893Sdimar5212GetAntennaSwitch(struct ath_hal *ah)
421249423Sdim{
422218893Sdim	return AH5212(ah)->ah_antControl;
423218893Sdim}
424218893Sdim
425218893SdimHAL_BOOL
426218893Sdimar5212SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING setting)
427218893Sdim{
428226633Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
429226633Sdim	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
430226633Sdim
431226633Sdim	if (!ahp->ah_phyPowerOn || chan == AH_NULL) {
432226633Sdim		/* PHY powered off, just stash settings */
433226633Sdim		ahp->ah_antControl = setting;
434226633Sdim		ahp->ah_diversity = (setting == HAL_ANT_VARIABLE);
435226633Sdim		return AH_TRUE;
436226633Sdim	}
437218893Sdim	return ar5212SetAntennaSwitchInternal(ah, setting, chan);
438276479Sdim}
439276479Sdim
440218893SdimHAL_BOOL
441218893Sdimar5212IsSleepAfterBeaconBroken(struct ath_hal *ah)
442218893Sdim{
443218893Sdim	return AH_TRUE;
444353358Sdim}
445218893Sdim
446212793SdimHAL_BOOL
447276479Sdimar5212SetSifsTime(struct ath_hal *ah, u_int us)
448218893Sdim{
449218893Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
450276479Sdim
451218893Sdim	if (us > ath_hal_mac_usec(ah, 0xffff)) {
452218893Sdim		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad SIFS time %u\n",
453218893Sdim		    __func__, us);
454218893Sdim		ahp->ah_sifstime = (u_int) -1;	/* restore default handling */
455218893Sdim		return AH_FALSE;
456218893Sdim	} else {
457276479Sdim		/* convert to system clocks */
458276479Sdim		OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, ath_hal_mac_clks(ah, us-2));
459276479Sdim		ahp->ah_sifstime = us;
460276479Sdim		return AH_TRUE;
461218893Sdim	}
462218893Sdim}
463218893Sdim
464249423Sdimu_int
465234353Sdimar5212GetSifsTime(struct ath_hal *ah)
466280031Sdim{
467218893Sdim	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SIFS) & 0xffff;
468218893Sdim	return ath_hal_mac_usec(ah, clks)+2;	/* convert from system clocks */
469218893Sdim}
470218893Sdim
471234353SdimHAL_BOOL
472234353Sdimar5212SetSlotTime(struct ath_hal *ah, u_int us)
473249423Sdim{
474249423Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
475249423Sdim
476276479Sdim	if (us < HAL_SLOT_TIME_6 || us > ath_hal_mac_usec(ah, 0xffff)) {
477234353Sdim		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad slot time %u\n",
478234353Sdim		    __func__, us);
479234353Sdim		ahp->ah_slottime = (u_int) -1;	/* restore default handling */
480218893Sdim		return AH_FALSE;
481218893Sdim	} else {
482234353Sdim		/* convert to system clocks */
483234353Sdim		OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath_hal_mac_clks(ah, us));
484234353Sdim		ahp->ah_slottime = us;
485234353Sdim		return AH_TRUE;
486234353Sdim	}
487234353Sdim}
488234353Sdim
489280031Sdimu_int
490280031Sdimar5212GetSlotTime(struct ath_hal *ah)
491280031Sdim{
492276479Sdim	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff;
493234353Sdim	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
494234353Sdim}
495234353Sdim
496234353SdimHAL_BOOL
497234353Sdimar5212SetAckTimeout(struct ath_hal *ah, u_int us)
498234353Sdim{
499280031Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
500353358Sdim
501280031Sdim	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
502280031Sdim		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad ack timeout %u\n",
503280031Sdim		    __func__, us);
504280031Sdim		ahp->ah_acktimeout = (u_int) -1; /* restore default handling */
505280031Sdim		return AH_FALSE;
506234353Sdim	} else {
507234353Sdim		/* convert to system clocks */
508280031Sdim		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
509234353Sdim			AR_TIME_OUT_ACK, ath_hal_mac_clks(ah, us));
510218893Sdim		ahp->ah_acktimeout = us;
511276479Sdim		return AH_TRUE;
512212793Sdim	}
513212793Sdim}
514276479Sdim
515212793Sdimu_int
516212793Sdimar5212GetAckTimeout(struct ath_hal *ah)
517280031Sdim{
518234353Sdim	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_ACK);
519276479Sdim	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
520280031Sdim}
521280031Sdim
522276479Sdimu_int
523218893Sdimar5212GetAckCTSRate(struct ath_hal *ah)
524280031Sdim{
525276479Sdim	return ((AH5212(ah)->ah_staId1Defaults & AR_STA_ID1_ACKCTS_6MB) == 0);
526280031Sdim}
527280031Sdim
528280031SdimHAL_BOOL
529276479Sdimar5212SetAckCTSRate(struct ath_hal *ah, u_int high)
530280031Sdim{
531212793Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
532212793Sdim
533309124Sdim	if (high) {
534309124Sdim		OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
535309124Sdim		ahp->ah_staId1Defaults &= ~AR_STA_ID1_ACKCTS_6MB;
536309124Sdim	} else {
537249423Sdim		OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
538218893Sdim		ahp->ah_staId1Defaults |= AR_STA_ID1_ACKCTS_6MB;
539296417Sdim	}
540218893Sdim	return AH_TRUE;
541314564Sdim}
542314564Sdim
543314564SdimHAL_BOOL
544314564Sdimar5212SetCTSTimeout(struct ath_hal *ah, u_int us)
545309124Sdim{
546280031Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
547280031Sdim
548243830Sdim	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
549218893Sdim		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad cts timeout %u\n",
550276479Sdim		    __func__, us);
551261991Sdim		ahp->ah_ctstimeout = (u_int) -1; /* restore default handling */
552261991Sdim		return AH_FALSE;
553276479Sdim	} else {
554261991Sdim		/* convert to system clocks */
555309124Sdim		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
556234353Sdim			AR_TIME_OUT_CTS, ath_hal_mac_clks(ah, us));
557276479Sdim		ahp->ah_ctstimeout = us;
558353358Sdim		return AH_TRUE;
559309124Sdim	}
560234353Sdim}
561276479Sdim
562276479Sdimu_int
563276479Sdimar5212GetCTSTimeout(struct ath_hal *ah)
564276479Sdim{
565309124Sdim	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_CTS);
566234353Sdim	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
567309124Sdim}
568234353Sdim
569276479Sdim/* Setup decompression for given key index */
570276479SdimHAL_BOOL
571234353Sdimar5212SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en)
572234353Sdim{
573276479Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
574234353Sdim
575234353Sdim        if (keyidx >= HAL_DECOMP_MASK_SIZE)
576276479Sdim                return HAL_EINVAL;
577234353Sdim        OS_REG_WRITE(ah, AR_DCM_A, keyidx);
578218893Sdim        OS_REG_WRITE(ah, AR_DCM_D, en ? AR_DCM_D_EN : 0);
579309124Sdim        ahp->ah_decompMask[keyidx] = en;
580243830Sdim
581243830Sdim        return AH_TRUE;
582234353Sdim}
583309124Sdim
584234353Sdim/* Setup coverage class */
585234353Sdimvoid
586234353Sdimar5212SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now)
587234353Sdim{
588234353Sdim	uint32_t slot, timeout, eifs;
589234353Sdim	u_int clkRate;
590234353Sdim
591234353Sdim	AH_PRIVATE(ah)->ah_coverageClass = coverageclass;
592234353Sdim
593309124Sdim	if (now) {
594276479Sdim		if (AH_PRIVATE(ah)->ah_coverageClass == 0)
595276479Sdim			return;
596309124Sdim
597234353Sdim		/* Don't apply coverage class to non A channels */
598243830Sdim		if (!IEEE80211_IS_CHAN_A(AH_PRIVATE(ah)->ah_curchan))
599234353Sdim			return;
600234353Sdim
601261991Sdim		/* Get core clock rate */
602276479Sdim		clkRate = ath_hal_mac_clks(ah, 1);
603276479Sdim
604276479Sdim		/* Compute EIFS */
605276479Sdim		slot = coverageclass * 3 * clkRate;
606243830Sdim		eifs = coverageclass * 6 * clkRate;
607243830Sdim		if (IEEE80211_IS_CHAN_HALF(AH_PRIVATE(ah)->ah_curchan)) {
608309124Sdim			slot += IFS_SLOT_HALF_RATE;
609243830Sdim			eifs += IFS_EIFS_HALF_RATE;
610243830Sdim		} else if (IEEE80211_IS_CHAN_QUARTER(AH_PRIVATE(ah)->ah_curchan)) {
611243830Sdim			slot += IFS_SLOT_QUARTER_RATE;
612243830Sdim			eifs += IFS_EIFS_QUARTER_RATE;
613276479Sdim		} else { /* full rate */
614243830Sdim			slot += IFS_SLOT_FULL_RATE;
615243830Sdim			eifs += IFS_EIFS_FULL_RATE;
616243830Sdim		}
617243830Sdim
618243830Sdim		/*
619234353Sdim		 * Add additional time for air propagation for ACK and CTS
620218893Sdim		 * timeouts. This value is in core clocks.
621223017Sdim  		 */
622243830Sdim		timeout = ACK_CTS_TIMEOUT_11A + (coverageclass * 3 * clkRate);
623234353Sdim
624223017Sdim		/*
625234353Sdim		 * Write the values: slot, eifs, ack/cts timeouts.
626276479Sdim		 */
627226633Sdim		OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot);
628223017Sdim		OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs);
629223017Sdim		OS_REG_WRITE(ah, AR_TIME_OUT,
630223017Sdim			  SM(timeout, AR_TIME_OUT_CTS)
631276479Sdim			| SM(timeout, AR_TIME_OUT_ACK));
632276479Sdim	}
633276479Sdim}
634276479Sdim
635223017SdimHAL_STATUS
636223017Sdimar5212SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration,
637223017Sdim    uint32_t nextStart, HAL_QUIET_FLAG flag)
638223017Sdim{
639218893Sdim	OS_REG_WRITE(ah, AR_QUIET2, period | (duration << AR_QUIET2_QUIET_DUR_S));
640218893Sdim	if (flag & HAL_QUIET_ENABLE) {
641218893Sdim		OS_REG_WRITE(ah, AR_QUIET1, nextStart | (1 << 16));
642280031Sdim	}
643288943Sdim	else {
644218893Sdim		OS_REG_WRITE(ah, AR_QUIET1, nextStart);
645212793Sdim	}
646249423Sdim	return HAL_OK;
647249423Sdim}
648218893Sdim
649218893Sdimvoid
650218893Sdimar5212SetPCUConfig(struct ath_hal *ah)
651218893Sdim{
652218893Sdim	ar5212SetOperatingMode(ah, AH_PRIVATE(ah)->ah_opmode);
653218893Sdim}
654218893Sdim
655218893Sdim/*
656218893Sdim * Return whether an external 32KHz crystal should be used
657218893Sdim * to reduce power consumption when sleeping.  We do so if
658218893Sdim * the crystal is present (obtained from EEPROM) and if we
659234353Sdim * are not running as an AP and are configured to use it.
660234353Sdim */
661280031SdimHAL_BOOL
662218893Sdimar5212Use32KHzclock(struct ath_hal *ah, HAL_OPMODE opmode)
663218893Sdim{
664218893Sdim	if (opmode != HAL_M_HOSTAP) {
665218893Sdim		struct ath_hal_5212 *ahp = AH5212(ah);
666239462Sdim		return ath_hal_eepromGetFlag(ah, AR_EEP_32KHZCRYSTAL) &&
667218893Sdim		       (ahp->ah_enable32kHzClock == USE_32KHZ ||
668218893Sdim		        ahp->ah_enable32kHzClock == AUTO_32KHZ);
669218893Sdim	} else
670218893Sdim		return AH_FALSE;
671218893Sdim}
672218893Sdim
673218893Sdim/*
674276479Sdim * If 32KHz clock exists, use it to lower power consumption during sleep
675234353Sdim *
676218893Sdim * Note: If clock is set to 32 KHz, delays on accessing certain
677276479Sdim *       baseband registers (27-31, 124-127) are required.
678276479Sdim */
679276479Sdimvoid
680218893Sdimar5212SetupClock(struct ath_hal *ah, HAL_OPMODE opmode)
681218893Sdim{
682218893Sdim	if (ar5212Use32KHzclock(ah, opmode)) {
683218893Sdim		/*
684218893Sdim		 * Enable clocks to be turned OFF in BB during sleep
685218893Sdim		 * and also enable turning OFF 32MHz/40MHz Refclk
686218893Sdim		 * from A2.
687218893Sdim		 */
688218893Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
689226633Sdim		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
690288943Sdim		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 0x14 : 0x18);
691218893Sdim		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32, 1);
692218893Sdim		OS_REG_WRITE(ah, AR_TSF_PARM, 61);  /* 32 KHz TSF incr */
693218893Sdim		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 1);
694218893Sdim
695218893Sdim		if (IS_2413(ah) || IS_5413(ah) || IS_2417(ah)) {
696212793Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x26);
697212793Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0d);
698212793Sdim			OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x07);
699212793Sdim			OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0x3f);
700212793Sdim			/* # Set sleep clock rate to 32 KHz. */
701226633Sdim			OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x2);
702212793Sdim		} else {
703218893Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x0a);
704212793Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0c);
705212793Sdim			OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x03);
706234353Sdim			OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0x20);
707212793Sdim			OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x3);
708218893Sdim		}
709212793Sdim	} else {
710212793Sdim		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x0);
711212793Sdim		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0);
712218893Sdim
713218893Sdim		OS_REG_WRITE(ah, AR_TSF_PARM, 1);	/* 32MHz TSF inc */
714212793Sdim
715276479Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
716212793Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x7f);
717212793Sdim
718218893Sdim		if (IS_2417(ah))
719218893Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0a);
720218893Sdim		else if (IS_HB63(ah))
721218893Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x32);
722218893Sdim		else
723218893Sdim			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e);
724218893Sdim		OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x0c);
725212793Sdim		OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0xff);
726212793Sdim		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
727212793Sdim		    IS_RAD5112_ANY(ah) || IS_5413(ah) || IS_2417(ah) ? 0x14 : 0x18);
728212793Sdim		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32,
729212793Sdim		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31);
730226633Sdim	}
731212793Sdim}
732212793Sdim
733212793Sdim/*
734212793Sdim * If 32KHz clock exists, turn it off and turn back on the 32Mhz
735218893Sdim */
736212793Sdimvoid
737218893Sdimar5212RestoreClock(struct ath_hal *ah, HAL_OPMODE opmode)
738212793Sdim{
739261991Sdim	if (ar5212Use32KHzclock(ah, opmode)) {
740218893Sdim		/* # Set sleep clock rate back to 32 MHz. */
741218893Sdim		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0);
742261991Sdim		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0);
743218893Sdim
744288943Sdim		OS_REG_WRITE(ah, AR_TSF_PARM, 1);	/* 32 MHz TSF incr */
745288943Sdim		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32,
746218893Sdim		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31);
747218893Sdim
748261991Sdim		/*
749218893Sdim		 * Restore BB registers to power-on defaults
750288943Sdim		 */
751218893Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
752218893Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x7f);
753218893Sdim		OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0e);
754218893Sdim		OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x0c);
755218893Sdim		OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0xff);
756218893Sdim		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
757218893Sdim		    IS_RAD5112_ANY(ah) || IS_5413(ah) ?  0x14 : 0x18);
758218893Sdim	}
759218893Sdim}
760218893Sdim
761218893Sdim/*
762218893Sdim * Adjust NF based on statistical values for 5GHz frequencies.
763314564Sdim * Default method: this may be overridden by the rf backend.
764276479Sdim */
765218893Sdimint16_t
766218893Sdimar5212GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c)
767218893Sdim{
768276479Sdim	static const struct {
769321369Sdim		uint16_t freqLow;
770218893Sdim		int16_t	  adjust;
771218893Sdim	} adjustDef[] = {
772218893Sdim		{ 5790,	11 },	/* NB: ordered high -> low */
773221345Sdim		{ 5730, 10 },
774221345Sdim		{ 5690,  9 },
775261991Sdim		{ 5660,  8 },
776261991Sdim		{ 5610,  7 },
777218893Sdim		{ 5530,  5 },
778221345Sdim		{ 5450,  4 },
779218893Sdim		{ 5379,  2 },
780221345Sdim		{ 5209,  0 },
781221345Sdim		{ 3000,  1 },
782221345Sdim		{    0,  0 },
783221345Sdim	};
784221345Sdim	int i;
785221345Sdim
786221345Sdim	for (i = 0; c->channel <= adjustDef[i].freqLow; i++)
787218893Sdim		;
788218893Sdim	return adjustDef[i].adjust;
789218893Sdim}
790218893Sdim
791218893SdimHAL_STATUS
792218893Sdimar5212GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
793218893Sdim	uint32_t capability, uint32_t *result)
794218893Sdim{
795218893Sdim#define	MACVERSION(ah)	AH_PRIVATE(ah)->ah_macVersion
796218893Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
797218893Sdim	const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
798218893Sdim	const struct ar5212AniState *ani;
799218893Sdim
800212793Sdim	switch (type) {
801218893Sdim	case HAL_CAP_CIPHER:		/* cipher handled in hardware */
802218893Sdim		switch (capability) {
803218893Sdim		case HAL_CIPHER_AES_CCM:
804218893Sdim			return pCap->halCipherAesCcmSupport ?
805218893Sdim				HAL_OK : HAL_ENOTSUPP;
806218893Sdim		case HAL_CIPHER_AES_OCB:
807218893Sdim		case HAL_CIPHER_TKIP:
808218893Sdim		case HAL_CIPHER_WEP:
809218893Sdim		case HAL_CIPHER_MIC:
810212793Sdim		case HAL_CIPHER_CLR:
811212793Sdim			return HAL_OK;
812218893Sdim		default:
813212793Sdim			return HAL_ENOTSUPP;
814218893Sdim		}
815218893Sdim	case HAL_CAP_TKIP_MIC:		/* handle TKIP MIC in hardware */
816218893Sdim		switch (capability) {
817212793Sdim		case 0:			/* hardware capability */
818249423Sdim			return HAL_OK;
819218893Sdim		case 1:
820249423Sdim			return (ahp->ah_staId1Defaults &
821234353Sdim			    AR_STA_ID1_CRPT_MIC_ENABLE) ?  HAL_OK : HAL_ENXIO;
822218893Sdim		}
823218893Sdim		return HAL_EINVAL;
824218893Sdim	case HAL_CAP_TKIP_SPLIT:	/* hardware TKIP uses split keys */
825218893Sdim		switch (capability) {
826234353Sdim		case 0:			/* hardware capability */
827234353Sdim			return pCap->halTkipMicTxRxKeySupport ?
828234353Sdim				HAL_ENXIO : HAL_OK;
829234353Sdim		case 1:			/* current setting */
830234353Sdim			return (ahp->ah_miscMode &
831234353Sdim			    AR_MISC_MODE_MIC_NEW_LOC_ENABLE) ? HAL_ENXIO : HAL_OK;
832234353Sdim		}
833234353Sdim		return HAL_EINVAL;
834234353Sdim	case HAL_CAP_WME_TKIPMIC:	/* hardware can do TKIP MIC w/ WMM */
835234353Sdim		/* XXX move to capability bit */
836234353Sdim		return MACVERSION(ah) > AR_SREV_VERSION_VENICE ||
837218893Sdim		    (MACVERSION(ah) == AR_SREV_VERSION_VENICE &&
838218893Sdim		     AH_PRIVATE(ah)->ah_macRev >= 8) ? HAL_OK : HAL_ENOTSUPP;
839218893Sdim	case HAL_CAP_DIVERSITY:		/* hardware supports fast diversity */
840218893Sdim		switch (capability) {
841218893Sdim		case 0:			/* hardware capability */
842218893Sdim			return HAL_OK;
843218893Sdim		case 1:			/* current setting */
844218893Sdim			return ahp->ah_diversity ? HAL_OK : HAL_ENXIO;
845218893Sdim		}
846218893Sdim		return HAL_EINVAL;
847218893Sdim	case HAL_CAP_DIAG:
848218893Sdim		*result = AH_PRIVATE(ah)->ah_diagreg;
849280031Sdim		return HAL_OK;
850280031Sdim	case HAL_CAP_TPC:
851218893Sdim		switch (capability) {
852234353Sdim		case 0:			/* hardware capability */
853276479Sdim			return HAL_OK;
854218893Sdim		case 1:
855218893Sdim			return ahp->ah_tpcEnabled ? HAL_OK : HAL_ENXIO;
856218893Sdim		}
857218893Sdim		return HAL_OK;
858218893Sdim	case HAL_CAP_PHYDIAG:		/* radar pulse detection capability */
859218893Sdim		switch (capability) {
860218893Sdim		case HAL_CAP_RADAR:
861218893Sdim			return ath_hal_eepromGetFlag(ah, AR_EEP_AMODE) ?
862212793Sdim			    HAL_OK: HAL_ENXIO;
863218893Sdim		case HAL_CAP_AR:
864280031Sdim			return (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) ||
865221345Sdim			    ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) ?
866218893Sdim			       HAL_OK: HAL_ENXIO;
867212793Sdim		}
868212793Sdim		return HAL_ENXIO;
869212793Sdim	case HAL_CAP_MCAST_KEYSRCH:	/* multicast frame keycache search */
870249423Sdim		switch (capability) {
871223017Sdim		case 0:			/* hardware capability */
872309124Sdim			return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENXIO;
873309124Sdim		case 1:
874218893Sdim			return (ahp->ah_staId1Defaults &
875218893Sdim			    AR_STA_ID1_MCAST_KSRCH) ? HAL_OK : HAL_ENXIO;
876249423Sdim		}
877218893Sdim		return HAL_EINVAL;
878221345Sdim	case HAL_CAP_TSF_ADJUST:	/* hardware has beacon tsf adjust */
879218893Sdim		switch (capability) {
880218893Sdim		case 0:			/* hardware capability */
881218893Sdim			return pCap->halTsfAddSupport ? HAL_OK : HAL_ENOTSUPP;
882218893Sdim		case 1:
883296417Sdim			return (ahp->ah_miscMode & AR_MISC_MODE_TX_ADD_TSF) ?
884296417Sdim				HAL_OK : HAL_ENXIO;
885296417Sdim		}
886218893Sdim		return HAL_EINVAL;
887218893Sdim	case HAL_CAP_TPC_ACK:
888212793Sdim		*result = MS(ahp->ah_macTPC, AR_TPC_ACK);
889234353Sdim		return HAL_OK;
890234353Sdim	case HAL_CAP_TPC_CTS:
891234353Sdim		*result = MS(ahp->ah_macTPC, AR_TPC_CTS);
892309124Sdim		return HAL_OK;
893234353Sdim	case HAL_CAP_INTMIT:		/* interference mitigation */
894234353Sdim		switch (capability) {
895234353Sdim		case HAL_CAP_INTMIT_PRESENT:		/* hardware capability */
896234353Sdim			return HAL_OK;
897234353Sdim		case HAL_CAP_INTMIT_ENABLE:
898218893Sdim			return (ahp->ah_procPhyErr & HAL_ANI_ENA) ?
899218893Sdim				HAL_OK : HAL_ENXIO;
900218893Sdim		case HAL_CAP_INTMIT_NOISE_IMMUNITY_LEVEL:
901218893Sdim		case HAL_CAP_INTMIT_OFDM_WEAK_SIGNAL_LEVEL:
902221345Sdim		case HAL_CAP_INTMIT_CCK_WEAK_SIGNAL_THR:
903234353Sdim		case HAL_CAP_INTMIT_FIRSTEP_LEVEL:
904234353Sdim		case HAL_CAP_INTMIT_SPUR_IMMUNITY_LEVEL:
905234353Sdim			ani = ar5212AniGetCurrentState(ah);
906309124Sdim			if (ani == AH_NULL)
907234353Sdim				return HAL_ENXIO;
908234353Sdim			switch (capability) {
909234353Sdim			case 2:	*result = ani->noiseImmunityLevel; break;
910234353Sdim			case 3: *result = !ani->ofdmWeakSigDetectOff; break;
911234353Sdim			case 4: *result = ani->cckWeakSigThreshold; break;
912212793Sdim			case 5: *result = ani->firstepLevel; break;
913218893Sdim			case 6: *result = ani->spurImmunityLevel; break;
914212793Sdim			}
915212793Sdim			return HAL_OK;
916360784Sdim		}
917234353Sdim		return HAL_EINVAL;
918212793Sdim	default:
919234353Sdim		return ath_hal_getcapability(ah, type, capability, result);
920234353Sdim	}
921234353Sdim#undef MACVERSION
922249423Sdim}
923218893Sdim
924218893SdimHAL_BOOL
925234353Sdimar5212SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
926234353Sdim	uint32_t capability, uint32_t setting, HAL_STATUS *status)
927234353Sdim{
928234353Sdim#define	N(a)	(sizeof(a)/sizeof(a[0]))
929234353Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
930234353Sdim	const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
931234353Sdim	uint32_t v;
932234353Sdim
933234353Sdim	switch (type) {
934234353Sdim	case HAL_CAP_TKIP_MIC:		/* handle TKIP MIC in hardware */
935234353Sdim		if (setting)
936234353Sdim			ahp->ah_staId1Defaults |= AR_STA_ID1_CRPT_MIC_ENABLE;
937234353Sdim		else
938234353Sdim			ahp->ah_staId1Defaults &= ~AR_STA_ID1_CRPT_MIC_ENABLE;
939234353Sdim		return AH_TRUE;
940261991Sdim	case HAL_CAP_TKIP_SPLIT:	/* hardware TKIP uses split keys */
941212793Sdim		if (!pCap->halTkipMicTxRxKeySupport)
942234353Sdim			return AH_FALSE;
943243830Sdim		/* NB: true =>'s use split key cache layout */
944243830Sdim		if (setting)
945243830Sdim			ahp->ah_miscMode &= ~AR_MISC_MODE_MIC_NEW_LOC_ENABLE;
946234353Sdim		else
947234353Sdim			ahp->ah_miscMode |= AR_MISC_MODE_MIC_NEW_LOC_ENABLE;
948234353Sdim		/* NB: write here so keys can be setup w/o a reset */
949234353Sdim		OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode);
950234353Sdim		return AH_TRUE;
951234353Sdim	case HAL_CAP_DIVERSITY:
952234353Sdim		if (ahp->ah_phyPowerOn) {
953234353Sdim			v = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
954234353Sdim			if (setting)
955261991Sdim				v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
956212793Sdim			else
957212793Sdim				v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
958243830Sdim			OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
959243830Sdim		}
960243830Sdim		ahp->ah_diversity = (setting != 0);
961234353Sdim		return AH_TRUE;
962234353Sdim	case HAL_CAP_DIAG:		/* hardware diagnostic support */
963234353Sdim		/*
964234353Sdim		 * NB: could split this up into virtual capabilities,
965234353Sdim		 *     (e.g. 1 => ACK, 2 => CTS, etc.) but it hardly
966234353Sdim		 *     seems worth the additional complexity.
967234353Sdim		 */
968234353Sdim		AH_PRIVATE(ah)->ah_diagreg = setting;
969261991Sdim		OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
970212793Sdim		return AH_TRUE;
971218893Sdim	case HAL_CAP_TPC:
972353358Sdim		ahp->ah_tpcEnabled = (setting != 0);
973234353Sdim		return AH_TRUE;
974234353Sdim	case HAL_CAP_MCAST_KEYSRCH:	/* multicast frame keycache search */
975234353Sdim		if (setting)
976234353Sdim			ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH;
977234353Sdim		else
978261991Sdim			ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH;
979234353Sdim		return AH_TRUE;
980234353Sdim	case HAL_CAP_TPC_ACK:
981234353Sdim	case HAL_CAP_TPC_CTS:
982234353Sdim		setting += ahp->ah_txPowerIndexOffset;
983218893Sdim		if (setting > 63)
984218893Sdim			setting = 63;
985353358Sdim		if (type == HAL_CAP_TPC_ACK) {
986353358Sdim			ahp->ah_macTPC &= AR_TPC_ACK;
987234353Sdim			ahp->ah_macTPC |= MS(setting, AR_TPC_ACK);
988234353Sdim		} else {
989234353Sdim			ahp->ah_macTPC &= AR_TPC_CTS;
990234353Sdim			ahp->ah_macTPC |= MS(setting, AR_TPC_CTS);
991234353Sdim		}
992218893Sdim		OS_REG_WRITE(ah, AR_TPC, ahp->ah_macTPC);
993218893Sdim		return AH_TRUE;
994218893Sdim	case HAL_CAP_INTMIT: {		/* interference mitigation */
995212793Sdim		/* This maps the public ANI commands to the internal ANI commands */
996218893Sdim		/* Private: HAL_ANI_CMD; Public: HAL_CAP_INTMIT_CMD */
997234353Sdim		static const HAL_ANI_CMD cmds[] = {
998218893Sdim			HAL_ANI_PRESENT,
999218893Sdim			HAL_ANI_MODE,
1000234353Sdim			HAL_ANI_NOISE_IMMUNITY_LEVEL,
1001234353Sdim			HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
1002321369Sdim			HAL_ANI_CCK_WEAK_SIGNAL_THR,
1003276479Sdim			HAL_ANI_FIRSTEP_LEVEL,
1004218893Sdim			HAL_ANI_SPUR_IMMUNITY_LEVEL,
1005234353Sdim		};
1006234353Sdim		return capability < N(cmds) ?
1007234353Sdim			AH5212(ah)->ah_aniControl(ah, cmds[capability], setting) :
1008234353Sdim			AH_FALSE;
1009234353Sdim	}
1010234353Sdim	case HAL_CAP_TSF_ADJUST:	/* hardware has beacon tsf adjust */
1011276479Sdim		if (pCap->halTsfAddSupport) {
1012234353Sdim			if (setting)
1013234353Sdim				ahp->ah_miscMode |= AR_MISC_MODE_TX_ADD_TSF;
1014234353Sdim			else
1015234353Sdim				ahp->ah_miscMode &= ~AR_MISC_MODE_TX_ADD_TSF;
1016212793Sdim			return AH_TRUE;
1017212793Sdim		}
1018212793Sdim		/* fall thru... */
1019218893Sdim	default:
1020226633Sdim		return ath_hal_setcapability(ah, type, capability,
1021226633Sdim				setting, status);
1022226633Sdim	}
1023226633Sdim#undef N
1024309124Sdim}
1025309124Sdim
1026309124SdimHAL_BOOL
1027309124Sdimar5212GetDiagState(struct ath_hal *ah, int request,
1028309124Sdim	const void *args, uint32_t argsize,
1029309124Sdim	void **result, uint32_t *resultsize)
1030309124Sdim{
1031309124Sdim	struct ath_hal_5212 *ahp = AH5212(ah);
1032309124Sdim
1033309124Sdim	(void) ahp;
1034309124Sdim	if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize))
1035309124Sdim		return AH_TRUE;
1036309124Sdim	switch (request) {
1037309124Sdim	case HAL_DIAG_EEPROM:
1038309124Sdim	case HAL_DIAG_EEPROM_EXP_11A:
1039218893Sdim	case HAL_DIAG_EEPROM_EXP_11B:
1040218893Sdim	case HAL_DIAG_EEPROM_EXP_11G:
1041218893Sdim	case HAL_DIAG_RFGAIN:
1042218893Sdim		return ath_hal_eepromDiag(ah, request,
1043212793Sdim		    args, argsize, result, resultsize);
1044218893Sdim	case HAL_DIAG_RFGAIN_CURSTEP:
1045218893Sdim		*result = __DECONST(void *, ahp->ah_gainValues.currStep);
1046218893Sdim		*resultsize = (*result == AH_NULL) ?
1047212793Sdim			0 : sizeof(GAIN_OPTIMIZATION_STEP);
1048360784Sdim		return AH_TRUE;
1049234353Sdim	case HAL_DIAG_PCDAC:
1050234353Sdim		*result = ahp->ah_pcdacTable;
1051280031Sdim		*resultsize = ahp->ah_pcdacTableSize;
1052218893Sdim		return AH_TRUE;
1053212793Sdim	case HAL_DIAG_TXRATES:
1054212793Sdim		*result = &ahp->ah_ratesArray[0];
1055249423Sdim		*resultsize = sizeof(ahp->ah_ratesArray);
1056234353Sdim		return AH_TRUE;
1057212793Sdim	case HAL_DIAG_ANI_CURRENT:
1058234353Sdim		*result = ar5212AniGetCurrentState(ah);
1059249423Sdim		*resultsize = (*result == AH_NULL) ?
1060218893Sdim			0 : sizeof(struct ar5212AniState);
1061212793Sdim		return AH_TRUE;
1062218893Sdim	case HAL_DIAG_ANI_STATS:
1063280031Sdim		*result = ar5212AniGetCurrentStats(ah);
1064280031Sdim		*resultsize = (*result == AH_NULL) ?
1065234353Sdim			0 : sizeof(struct ar5212Stats);
1066261991Sdim		return AH_TRUE;
1067276479Sdim	case HAL_DIAG_ANI_CMD:
1068218893Sdim		if (argsize != 2*sizeof(uint32_t))
1069218893Sdim			return AH_FALSE;
1070218893Sdim		AH5212(ah)->ah_aniControl(ah, ((const uint32_t *)args)[0],
1071314564Sdim			((const uint32_t *)args)[1]);
1072314564Sdim		return AH_TRUE;
1073218893Sdim	case HAL_DIAG_ANI_PARAMS:
1074234353Sdim		/*
1075234353Sdim		 * NB: We assume struct ar5212AniParams is identical
1076234353Sdim		 * to HAL_ANI_PARAMS; if they diverge then we'll need
1077234353Sdim		 * to handle it here
1078234353Sdim		 */
1079234353Sdim		if (argsize == 0 && args == AH_NULL) {
1080234353Sdim			struct ar5212AniState *aniState =
1081234353Sdim			    ar5212AniGetCurrentState(ah);
1082218893Sdim			if (aniState == AH_NULL)
1083218893Sdim				return AH_FALSE;
1084243830Sdim			*result = __DECONST(void *, aniState->params);
1085243830Sdim			*resultsize = sizeof(struct ar5212AniParams);
1086243830Sdim			return AH_TRUE;
1087234353Sdim		} else {
1088234353Sdim			if (argsize != sizeof(struct ar5212AniParams))
1089234353Sdim				return AH_FALSE;
1090234353Sdim			return ar5212AniSetParams(ah, args, args);
1091234353Sdim		}
1092234353Sdim	}
1093234353Sdim	return AH_FALSE;
1094234353Sdim}
1095234353Sdim
1096218893Sdim/*
1097218893Sdim * Check whether there's an in-progress NF completion.
1098243830Sdim *
1099243830Sdim * Returns AH_TRUE if there's a in-progress NF calibration, AH_FALSE
1100243830Sdim * otherwise.
1101234353Sdim */
1102234353SdimHAL_BOOL
1103234353Sdimar5212IsNFCalInProgress(struct ath_hal *ah)
1104234353Sdim{
1105234353Sdim	if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)
1106234353Sdim		return AH_TRUE;
1107234353Sdim	return AH_FALSE;
1108234353Sdim}
1109218893Sdim
1110212793Sdim/*
1111353358Sdim * Wait for an in-progress NF calibration to complete.
1112234353Sdim *
1113234353Sdim * The completion function waits "i" times 10uS.
1114234353Sdim * It returns AH_TRUE if the NF calibration completed (or was never
1115276479Sdim * in progress); AH_FALSE if it was still in progress after "i" checks.
1116234353Sdim */
1117234353SdimHAL_BOOL
1118234353Sdimar5212WaitNFCalComplete(struct ath_hal *ah, int i)
1119234353Sdim{
1120234353Sdim	int j;
1121234353Sdim	if (i <= 0)
1122234353Sdim		i = 1;	  /* it should run at least once */
1123234353Sdim	for (j = 0; j < i; j++) {
1124212793Sdim		if (! ar5212IsNFCalInProgress(ah))
1125212793Sdim			return AH_TRUE;
1126353358Sdim		OS_DELAY(10);
1127353358Sdim	}
1128234353Sdim	return AH_FALSE;
1129234353Sdim}
1130234353Sdim
1131234353Sdimvoid
1132234353Sdimar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
1133212793Sdim{
1134212793Sdim	uint32_t val;
1135212793Sdim	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
1136218893Sdim
1137234353Sdim	if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
1138218893Sdim		val &= ~AR_PHY_RADAR_0_FIRPWR;
1139218893Sdim		val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
1140261991Sdim	}
1141276479Sdim	if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
1142218893Sdim		val &= ~AR_PHY_RADAR_0_RRSSI;
1143234353Sdim		val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
1144234353Sdim	}
1145212793Sdim	if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
1146212793Sdim		val &= ~AR_PHY_RADAR_0_HEIGHT;
1147212793Sdim		val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
1148218893Sdim	}
1149212793Sdim	if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
1150212793Sdim		val &= ~AR_PHY_RADAR_0_PRSSI;
1151212793Sdim		val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
1152226633Sdim	}
1153226633Sdim	if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
1154226633Sdim		val &= ~AR_PHY_RADAR_0_INBAND;
1155226633Sdim		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
1156309124Sdim	}
1157309124Sdim	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val | AR_PHY_RADAR_0_ENA);
1158309124Sdim}
1159309124Sdim
1160309124Sdimvoid
1161309124Sdimar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
1162309124Sdim{
1163309124Sdim	uint32_t val,temp;
1164309124Sdim
1165309124Sdim	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
1166309124Sdim
1167309124Sdim	temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
1168309124Sdim	temp |= 0xFFFFFF80;
1169309124Sdim	pe->pe_firpwr = temp;
1170309124Sdim	pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
1171218893Sdim	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
1172218893Sdim	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
1173212793Sdim	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
1174212793Sdim
1175212793Sdim	pe->pe_relpwr = 0;
1176212793Sdim	pe->pe_relstep = 0;
1177212793Sdim	pe->pe_maxlen = 0;
1178218893Sdim	pe->pe_extchannel = AH_FALSE;
1179218893Sdim}
1180218893Sdim
1181218893Sdim/*
1182218893Sdim * Process the radar phy error and extract the pulse duration.
1183212793Sdim */
1184212793SdimHAL_BOOL
1185234353Sdimar5212ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
1186234353Sdim    uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
1187212793Sdim{
1188212793Sdim	uint8_t dur;
1189212793Sdim	uint8_t rssi;
1190218893Sdim
1191218893Sdim	/* Check whether the given phy error is a radar event */
1192218893Sdim	if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) &&
1193218893Sdim	    (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT))
1194218893Sdim		return AH_FALSE;
1195218893Sdim
1196218893Sdim	/*
1197218893Sdim	 * The first byte is the pulse width - if there's
1198218893Sdim	 * no data, simply set the duration to 0
1199218893Sdim	 */
1200218893Sdim	if (rxs->rs_datalen >= 1)
1201218893Sdim		/* The pulse width is byte 0 of the data */
1202218893Sdim		dur = ((uint8_t) buf[0]) & 0xff;
1203218893Sdim	else
1204218893Sdim		dur = 0;
1205218893Sdim
1206218893Sdim	/* Pulse RSSI is the normal reported RSSI */
1207218893Sdim	rssi = (uint8_t) rxs->rs_rssi;
1208218893Sdim
1209218893Sdim	/* 0 duration/rssi is not a valid radar event */
1210218893Sdim	if (dur == 0 && rssi == 0)
1211218893Sdim		return AH_FALSE;
1212218893Sdim
1213218893Sdim	HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n",
1214218893Sdim	    __func__, rssi, dur);
1215218893Sdim
1216218893Sdim	/* Record the event */
1217218893Sdim	event->re_full_ts = fulltsf;
1218218893Sdim	event->re_ts = rxs->rs_tstamp;
1219218893Sdim	event->re_rssi = rssi;
1220218893Sdim	event->re_dur = dur;
1221218893Sdim	event->re_flags = HAL_DFS_EVENT_PRICH;
1222218893Sdim
1223218893Sdim	return AH_TRUE;
1224218893Sdim}
1225218893Sdim