118334Speter/*
290075Sobrien * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
390075Sobrien * Copyright (c) 2002-2008 Atheros Communications, Inc.
418334Speter *
518334Speter * Permission to use, copy, modify, and/or distribute this software for any
618334Speter * purpose with or without fee is hereby granted, provided that the above
718334Speter * copyright notice and this permission notice appear in all copies.
818334Speter *
918334Speter * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1018334Speter * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1118334Speter * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1218334Speter * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1318334Speter * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1418334Speter * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1518334Speter * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1618334Speter *
1718334Speter * $Id: ar5212_misc.c,v 1.3 2012/02/12 13:48:45 wiz Exp $
1818334Speter */
1918334Speter#include "opt_ah.h"
2018334Speter
2118334Speter#include "ah.h"
2218334Speter#include "ah_internal.h"
2318334Speter#include "ah_devid.h"
2418334Speter#ifdef AH_DEBUG
2518334Speter#include "ah_desc.h"			/* NB: for HAL_PHYERR* */
2618334Speter#endif
2718334Speter
2818334Speter#include "ar5212/ar5212.h"
2918334Speter#include "ar5212/ar5212reg.h"
3050397Sobrien#include "ar5212/ar5212phy.h"
3118334Speter
3218334Speter#include "ah_eeprom_v3.h"
3318334Speter
3418334Speter#define	AR_NUM_GPIO	6		/* 6 GPIO pins */
3552284Sobrien#define	AR_GPIOD_MASK	0x0000002F	/* GPIO data reg r/w mask */
3652284Sobrien
3718334Speterextern void ar5212SetRateDurationTable(struct ath_hal *, HAL_CHANNEL *);
3890075Sobrien
3990075Sobrienvoid
4090075Sobrienar5212GetMacAddress(struct ath_hal *ah, uint8_t *mac)
4190075Sobrien{
4250397Sobrien	struct ath_hal_5212 *ahp = AH5212(ah);
4318334Speter
4418334Speter	OS_MEMCPY(mac, ahp->ah_macaddr, IEEE80211_ADDR_LEN);
4518334Speter}
4618334Speter
4718334SpeterHAL_BOOL
4818334Speterar5212SetMacAddress(struct ath_hal *ah, const uint8_t *mac)
4918334Speter{
5018334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
5118334Speter
5218334Speter	OS_MEMCPY(ahp->ah_macaddr, mac, IEEE80211_ADDR_LEN);
5318334Speter	return AH_TRUE;
5418334Speter}
5518334Speter
5618334Spetervoid
5718334Speterar5212GetBssIdMask(struct ath_hal *ah, uint8_t *mask)
5818334Speter{
5918334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
6018334Speter
6118334Speter	OS_MEMCPY(mask, ahp->ah_bssidmask, IEEE80211_ADDR_LEN);
6218334Speter}
6318334Speter
6418334SpeterHAL_BOOL
6518334Speterar5212SetBssIdMask(struct ath_hal *ah, const uint8_t *mask)
6618334Speter{
6718334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
6818334Speter
6918334Speter	/* save it since it must be rewritten on reset */
7090075Sobrien	OS_MEMCPY(ahp->ah_bssidmask, mask, IEEE80211_ADDR_LEN);
7190075Sobrien
7290075Sobrien	OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask));
7350397Sobrien	OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4));
7418334Speter	return AH_TRUE;
7590075Sobrien}
7618334Speter
7790075Sobrien/*
7818334Speter * Attempt to change the cards operating regulatory domain to the given value
7918334Speter */
8018334SpeterHAL_BOOL
8150397Sobrienar5212SetRegulatoryDomain(struct ath_hal *ah,
8218334Speter	uint16_t regDomain, HAL_STATUS *status)
8350397Sobrien{
8450397Sobrien	HAL_STATUS ecode;
8550397Sobrien
8690075Sobrien	if (AH_PRIVATE(ah)->ah_currentRD == regDomain) {
8750397Sobrien		ecode = HAL_EINVAL;
8890075Sobrien		goto bad;
8950397Sobrien	}
9050397Sobrien	if (ath_hal_eepromGetFlag(ah, AR_EEP_WRITEPROTECT)) {
9150397Sobrien		ecode = HAL_EEWRITE;
9250397Sobrien		goto bad;
9352284Sobrien	}
9450397Sobrien#ifdef AH_SUPPORT_WRITE_REGDOMAIN
9550397Sobrien	if (ath_hal_eepromWrite(ah, AR_EEPROM_REG_DOMAIN, regDomain)) {
9650397Sobrien		HALDEBUG(ah, HAL_DEBUG_ANY,
9790075Sobrien		    "%s: set regulatory domain to %u (0x%x)\n",
9850397Sobrien		    __func__, regDomain, regDomain);
9950397Sobrien		AH_PRIVATE(ah)->ah_currentRD = regDomain;
10050397Sobrien		return AH_TRUE;
10150397Sobrien	}
10250397Sobrien#endif
10350397Sobrien	ecode = HAL_EIO;
10450397Sobrienbad:
10550397Sobrien	if (status)
10690075Sobrien		*status = ecode;
10750397Sobrien	return AH_FALSE;
10850397Sobrien}
10950397Sobrien
11050397Sobrien/*
11150397Sobrien * Return the wireless modes (a,b,g,t) supported by hardware.
11250397Sobrien *
11352284Sobrien * This value is what is actually supported by the hardware
11450397Sobrien * and is unaffected by regulatory/country code settings.
11550397Sobrien */
11650397Sobrienu_int
11750397Sobrienar5212GetWirelessModes(struct ath_hal *ah)
11850397Sobrien{
11950397Sobrien	u_int mode = 0;
12050397Sobrien
12150397Sobrien	if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) {
12250397Sobrien		mode = HAL_MODE_11A;
12350397Sobrien		if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE))
12450397Sobrien			mode |= HAL_MODE_TURBO | HAL_MODE_108A;
12550397Sobrien		if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate)
12690075Sobrien			mode |= HAL_MODE_11A_HALF_RATE;
12750397Sobrien		if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate)
12850397Sobrien			mode |= HAL_MODE_11A_QUARTER_RATE;
12950397Sobrien	}
13050397Sobrien	if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE))
13150397Sobrien		mode |= HAL_MODE_11B;
13250397Sobrien	if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) &&
13318334Speter	    AH_PRIVATE(ah)->ah_subvendorid != AR_SUBVENDOR_ID_NOG) {
13418334Speter		mode |= HAL_MODE_11G;
13552284Sobrien		if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO2DISABLE))
13618334Speter			mode |= HAL_MODE_108G;
13718334Speter		if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate)
13818334Speter			mode |= HAL_MODE_11G_HALF_RATE;
13918334Speter		if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate)
14052284Sobrien			mode |= HAL_MODE_11G_QUARTER_RATE;
14118334Speter	}
14250397Sobrien	return mode;
14350397Sobrien}
14490075Sobrien
14518334Speter/*
14618334Speter * Set the interrupt and GPIO values so the ISR can disable RF
14790075Sobrien * on a switch signal.  Assumes GPIO port and interrupt polarity
14890075Sobrien * are set prior to call.
14990075Sobrien */
15090075Sobrienvoid
15190075Sobrienar5212EnableRfKill(struct ath_hal *ah)
15290075Sobrien{
15318334Speter	uint16_t rfsilent = AH_PRIVATE(ah)->ah_rfsilent;
15490075Sobrien	int select = MS(rfsilent, AR_EEPROM_RFSILENT_GPIO_SEL);
15590075Sobrien	int polarity = MS(rfsilent, AR_EEPROM_RFSILENT_POLARITY);
15690075Sobrien
15718334Speter	/*
15818334Speter	 * Configure the desired GPIO port for input
15990075Sobrien	 * and enable baseband rf silence.
16090075Sobrien	 */
16118334Speter	ath_hal_gpioCfgInput(ah, select);
16218334Speter	OS_REG_SET_BIT(ah, AR_PHY(0), 0x00002000);
16390075Sobrien	/*
16490075Sobrien	 * If radio disable switch connection to GPIO bit x is enabled
16590075Sobrien	 * program GPIO interrupt.
16690075Sobrien	 * If rfkill bit on eeprom is 1, setupeeprommap routine has already
16718334Speter	 * verified that it is a later version of eeprom, it has a place for
16890075Sobrien	 * rfkill bit and it is set to 1, indicating that GPIO bit x hardware
16990075Sobrien	 * connection is present.
17090075Sobrien	 */
17118334Speter	ath_hal_gpioSetIntr(ah, select,
17290075Sobrien	    (ath_hal_gpioGet(ah, select) == polarity ? !polarity : polarity));
17318334Speter}
17418334Speter
17518334Speter/*
17652284Sobrien * Change the LED blinking pattern to correspond to the connectivity
17718334Speter */
17890075Sobrienvoid
17990075Sobrienar5212SetLedState(struct ath_hal *ah, HAL_LED_STATE state)
18090075Sobrien{
18150397Sobrien	static const uint32_t ledbits[8] = {
18290075Sobrien		AR_PCICFG_LEDCTL_NONE,	/* HAL_LED_INIT */
18350397Sobrien		AR_PCICFG_LEDCTL_PEND,	/* HAL_LED_SCAN */
18490075Sobrien		AR_PCICFG_LEDCTL_PEND,	/* HAL_LED_AUTH */
18590075Sobrien		AR_PCICFG_LEDCTL_ASSOC,	/* HAL_LED_ASSOC*/
18690075Sobrien		AR_PCICFG_LEDCTL_ASSOC,	/* HAL_LED_RUN */
18790075Sobrien		AR_PCICFG_LEDCTL_NONE,
18850397Sobrien		AR_PCICFG_LEDCTL_NONE,
18990075Sobrien		AR_PCICFG_LEDCTL_NONE,
19050397Sobrien	};
19150397Sobrien	uint32_t bits;
19218334Speter
19318334Speter	bits = OS_REG_READ(ah, AR_PCICFG);
19490075Sobrien	if (IS_2417(ah)) {
19590075Sobrien		/*
19690075Sobrien		 * Enable LED for Nala. There is a bit marked reserved
19790075Sobrien		 * that must be set and we also turn on the power led.
19890075Sobrien		 * Because we mark s/w LED control setting the control
19990075Sobrien		 * status bits below is meangless (the driver must flash
20090075Sobrien		 * the LED(s) using the GPIO lines).
20190075Sobrien		 */
20290075Sobrien		bits = (bits &~ AR_PCICFG_LEDMODE)
20390075Sobrien		     | SM(AR_PCICFG_LEDMODE_POWON, AR_PCICFG_LEDMODE)
20490075Sobrien#if 0
20590075Sobrien		     | SM(AR_PCICFG_LEDMODE_NETON, AR_PCICFG_LEDMODE)
20690075Sobrien#endif
20790075Sobrien		     | 0x08000000;
20890075Sobrien	}
20990075Sobrien	bits = (bits &~ AR_PCICFG_LEDCTL)
21090075Sobrien	     | SM(ledbits[state & 0x7], AR_PCICFG_LEDCTL);
21190075Sobrien	OS_REG_WRITE(ah, AR_PCICFG, bits);
21290075Sobrien}
21390075Sobrien
21490075Sobrien/*
21590075Sobrien * Change association related fields programmed into the hardware.
21690075Sobrien * Writing a valid BSSID to the hardware effectively enables the hardware
21718334Speter * to synchronize its TSF to the correct beacons and receive frames coming
21852284Sobrien * from that BSSID. It is called by the SME JOIN operation.
21918334Speter */
22090075Sobrienvoid
22118334Speterar5212WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId)
22218334Speter{
22318334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
22418334Speter
22550397Sobrien	/* XXX save bssid for possible re-use on reset */
22650397Sobrien	OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN);
22750397Sobrien	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
22818334Speter	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) |
22952284Sobrien				     ((assocId & 0x3fff)<<AR_BSS_ID1_AID_S));
23090075Sobrien}
23152284Sobrien
23252284Sobrien/*
23390075Sobrien * Get the current hardware tsf for stamlme
23452284Sobrien */
23552284Sobrienuint64_t
23652284Sobrienar5212GetTsf64(struct ath_hal *ah)
23718334Speter{
23818334Speter	uint32_t low1, low2, u32;
23918334Speter
24018334Speter	/* sync multi-word read */
24118334Speter	low1 = OS_REG_READ(ah, AR_TSF_L32);
24252284Sobrien	u32 = OS_REG_READ(ah, AR_TSF_U32);
24352284Sobrien	low2 = OS_REG_READ(ah, AR_TSF_L32);
24490075Sobrien	if (low2 < low1) {	/* roll over */
24590075Sobrien		/*
24690075Sobrien		 * If we are not preempted this will work.  If we are
24790075Sobrien		 * then we re-reading AR_TSF_U32 does no good as the
24890075Sobrien		 * low bits will be meaningless.  Likewise reading
24990075Sobrien		 * L32, U32, U32, then comparing the last two reads
25090075Sobrien		 * to check for rollover doesn't help if preempted--so
25118334Speter		 * we take this approach as it costs one less PCI read
25218334Speter		 * which can be noticeable when doing things like
25318334Speter		 * timestamping packets in monitor mode.
25418334Speter		 */
25518334Speter		u32++;
25618334Speter	}
25750397Sobrien	return (((uint64_t) u32) << 32) | ((uint64_t) low2);
25818334Speter}
25950397Sobrien
26018334Speter/*
26118334Speter * Get the current hardware tsf for stamlme
26218334Speter */
26318334Speteruint32_t
26418334Speterar5212GetTsf32(struct ath_hal *ah)
26518334Speter{
26618334Speter	return OS_REG_READ(ah, AR_TSF_L32);
26718334Speter}
26818334Speter
26952284Sobrien/*
27090075Sobrien * Reset the current hardware tsf for stamlme.
27152284Sobrien */
27290075Sobrienvoid
27318334Speterar5212ResetTsf(struct ath_hal *ah)
27418334Speter{
27518334Speter
27618334Speter	uint32_t val = OS_REG_READ(ah, AR_BEACON);
27718334Speter
27818334Speter	OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF);
27918334Speter	/*
28050397Sobrien	 * When resetting the TSF, write twice to the
28118334Speter	 * corresponding register; each write to the RESET_TSF bit toggles
28218334Speter	 * the internal signal to cause a reset of the TSF - but if the signal
28318334Speter	 * is left high, it will reset the TSF on the next chip reset also!
28418334Speter	 * writing the bit an even number of times fixes this issue
28518334Speter	 */
28618334Speter	OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF);
28718334Speter}
28818334Speter
28918334Speter/*
29018334Speter * Set or clear hardware basic rate bit
29118334Speter * Set hardware basic rate set if basic rate is found
29218334Speter * and basic rate is equal or less than 2Mbps
29318334Speter */
29418334Spetervoid
29518334Speterar5212SetBasicRate(struct ath_hal *ah, HAL_RATE_SET *rs)
29618334Speter{
29718334Speter	HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan;
29818334Speter	uint32_t reg;
29918334Speter	uint8_t xset;
30018334Speter	int i;
30150397Sobrien
30250397Sobrien	if (chan == AH_NULL || !IS_CHAN_CCK(chan))
30318334Speter		return;
30418334Speter	xset = 0;
30518334Speter	for (i = 0; i < rs->rs_count; i++) {
30690075Sobrien		uint8_t rset = rs->rs_rates[i];
30790075Sobrien		/* Basic rate defined? */
30890075Sobrien		if ((rset & 0x80) && (rset &= 0x7f) >= xset)
30990075Sobrien			xset = rset;
31090075Sobrien	}
31118334Speter	/*
31290075Sobrien	 * Set the h/w bit to reflect whether or not the basic
31390075Sobrien	 * rate is found to be equal or less than 2Mbps.
31490075Sobrien	 */
31518334Speter	reg = OS_REG_READ(ah, AR_STA_ID1);
31690075Sobrien	if (xset && xset/2 <= 2)
31790075Sobrien		OS_REG_WRITE(ah, AR_STA_ID1, reg | AR_STA_ID1_BASE_RATE_11B);
31890075Sobrien	else
31918334Speter		OS_REG_WRITE(ah, AR_STA_ID1, reg &~ AR_STA_ID1_BASE_RATE_11B);
32090075Sobrien}
32190075Sobrien
32290075Sobrien/*
32390075Sobrien * Grab a semi-random value from hardware registers - may not
32490075Sobrien * change often
32590075Sobrien */
32690075Sobrienuint32_t
32790075Sobrienar5212GetRandomSeed(struct ath_hal *ah)
32890075Sobrien{
32990075Sobrien	uint32_t nf;
33018334Speter
33190075Sobrien	nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff;
33218334Speter	if (nf & 0x100)
33318334Speter		nf = 0 - ((nf ^ 0x1ff) + 1);
33418334Speter	return (OS_REG_READ(ah, AR_TSF_U32) ^
33590075Sobrien		OS_REG_READ(ah, AR_TSF_L32) ^ nf);
33618334Speter}
33718334Speter
33818334Speter/*
33918334Speter * Detect if our card is present
34018334Speter */
34118334SpeterHAL_BOOL
34218334Speterar5212DetectCardPresent(struct ath_hal *ah)
34390075Sobrien{
34490075Sobrien	uint16_t macVersion, macRev;
34550397Sobrien	uint32_t v;
34618334Speter
34790075Sobrien	/*
34890075Sobrien	 * Read the Silicon Revision register and compare that
34952284Sobrien	 * to what we read at attach time.  If the same, we say
35018334Speter	 * a card/device is present.
35150397Sobrien	 */
35218334Speter	v = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID;
35318334Speter	macVersion = v >> AR_SREV_ID_S;
35490075Sobrien	macRev = v & AR_SREV_REVISION;
35518334Speter	return (AH_PRIVATE(ah)->ah_macVersion == macVersion &&
35618334Speter		AH_PRIVATE(ah)->ah_macRev == macRev);
35750397Sobrien}
35850397Sobrien
35950397Sobrienvoid
36090075Sobrienar5212EnableMibCounters(struct ath_hal *ah)
36190075Sobrien{
36250397Sobrien	/* NB: this just resets the mib counter machinery */
36390075Sobrien	OS_REG_WRITE(ah, AR_MIBC,
36490075Sobrien	    ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);
36590075Sobrien}
36690075Sobrien
36790075Sobrienvoid
36890075Sobrienar5212DisableMibCounters(struct ath_hal *ah)
36990075Sobrien{
37090075Sobrien	OS_REG_WRITE(ah, AR_MIBC,  AR_MIBC | AR_MIBC_CMC);
37190075Sobrien}
37290075Sobrien
37390075Sobrien/*
37490075Sobrien * Update MIB Counters
37590075Sobrien */
37690075Sobrienvoid
37750397Sobrienar5212UpdateMibCounters(struct ath_hal *ah, HAL_MIB_STATS* stats)
37850397Sobrien{
37990075Sobrien	stats->ackrcv_bad += OS_REG_READ(ah, AR_ACK_FAIL);
38090075Sobrien	stats->rts_bad	  += OS_REG_READ(ah, AR_RTS_FAIL);
38190075Sobrien	stats->fcs_bad	  += OS_REG_READ(ah, AR_FCS_FAIL);
38250397Sobrien	stats->rts_good	  += OS_REG_READ(ah, AR_RTS_OK);
38390075Sobrien	stats->beacons	  += OS_REG_READ(ah, AR_BEACON_CNT);
38490075Sobrien}
38550397Sobrien
38690075Sobrien/*
38752284Sobrien * Detect if the HW supports spreading a CCK signal on channel 14
38850397Sobrien */
38950397SobrienHAL_BOOL
39090075Sobrienar5212IsJapanChannelSpreadSupported(struct ath_hal *ah)
39150397Sobrien{
39290075Sobrien	return AH_TRUE;
39350397Sobrien}
39450397Sobrien
39550397Sobrien/*
39652284Sobrien * Get the rssi of frame curently being received.
39752284Sobrien */
39852284Sobrienuint32_t
39918334Speterar5212GetCurRssi(struct ath_hal *ah)
40018334Speter{
40118334Speter	return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff);
40218334Speter}
40318334Speter
40490075Sobrienu_int
40590075Sobrienar5212GetDefAntenna(struct ath_hal *ah)
40618334Speter{
40718334Speter	return (OS_REG_READ(ah, AR_DEF_ANTENNA) & 0x7);
40818334Speter}
40918334Speter
41090075Sobrienvoid
41118334Speterar5212SetDefAntenna(struct ath_hal *ah, u_int antenna)
41218334Speter{
41350397Sobrien	OS_REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
41450397Sobrien}
41550397Sobrien
41650397SobrienHAL_ANT_SETTING
41790075Sobrienar5212GetAntennaSwitch(struct ath_hal *ah)
41890075Sobrien{
41990075Sobrien	return AH5212(ah)->ah_antControl;
42090075Sobrien}
42190075Sobrien
42290075SobrienHAL_BOOL
42390075Sobrienar5212SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING setting)
42418334Speter{
42518334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
42618334Speter	const HAL_CHANNEL_INTERNAL *ichan = AH_PRIVATE(ah)->ah_curchan;
42790075Sobrien
42890075Sobrien	if (!ahp->ah_phyPowerOn || ichan == AH_NULL) {
42990075Sobrien		/* PHY powered off, just stash settings */
43090075Sobrien		ahp->ah_antControl = setting;
43190075Sobrien		ahp->ah_diversity = (setting == HAL_ANT_VARIABLE);
43290075Sobrien		return AH_TRUE;
43390075Sobrien	}
43490075Sobrien	return ar5212SetAntennaSwitchInternal(ah, setting, ichan);
43590075Sobrien}
43690075Sobrien
43790075SobrienHAL_BOOL
43890075Sobrienar5212IsSleepAfterBeaconBroken(struct ath_hal *ah)
43990075Sobrien{
44090075Sobrien	return AH_TRUE;
44190075Sobrien}
44290075Sobrien
44390075SobrienHAL_BOOL
44490075Sobrienar5212SetSifsTime(struct ath_hal *ah, u_int us)
44590075Sobrien{
44690075Sobrien	struct ath_hal_5212 *ahp = AH5212(ah);
44790075Sobrien
44890075Sobrien	if (us > ath_hal_mac_usec(ah, 0xffff)) {
44990075Sobrien		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad SIFS time %u\n",
45090075Sobrien		    __func__, us);
45190075Sobrien		ahp->ah_sifstime = (u_int) -1;	/* restore default handling */
45290075Sobrien		return AH_FALSE;
45390075Sobrien	} else {
45490075Sobrien		/* convert to system clocks */
45590075Sobrien		OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, ath_hal_mac_clks(ah, us-2));
45690075Sobrien		ahp->ah_slottime = us;
45790075Sobrien		return AH_TRUE;
45818334Speter	}
45918334Speter}
46018334Speter
46118334Speteru_int
46218334Speterar5212GetSifsTime(struct ath_hal *ah)
46318334Speter{
46418334Speter	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SIFS) & 0xffff;
46518334Speter	return ath_hal_mac_usec(ah, clks)+2;	/* convert from system clocks */
46618334Speter}
46718334Speter
46818334SpeterHAL_BOOL
46918334Speterar5212SetSlotTime(struct ath_hal *ah, u_int us)
47018334Speter{
47118334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
47218334Speter
47318334Speter	if (us < HAL_SLOT_TIME_6 || us > ath_hal_mac_usec(ah, 0xffff)) {
47418334Speter		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad slot time %u\n",
47518334Speter		    __func__, us);
47618334Speter		ahp->ah_slottime = (u_int) -1;	/* restore default handling */
47752284Sobrien		return AH_FALSE;
47852284Sobrien	} else {
47952284Sobrien		/* convert to system clocks */
48090075Sobrien		OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath_hal_mac_clks(ah, us));
48190075Sobrien		ahp->ah_slottime = us;
48252284Sobrien		return AH_TRUE;
48352284Sobrien	}
48452284Sobrien}
48552284Sobrien
48652284Sobrienu_int
48752284Sobrienar5212GetSlotTime(struct ath_hal *ah)
48890075Sobrien{
48918334Speter	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff;
49018334Speter	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
49118334Speter}
49218334Speter
49318334SpeterHAL_BOOL
49418334Speterar5212SetAckTimeout(struct ath_hal *ah, u_int us)
49518334Speter{
49618334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
49718334Speter
49818334Speter	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
49950397Sobrien		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad ack timeout %u\n",
50052284Sobrien		    __func__, us);
50118334Speter		ahp->ah_acktimeout = (u_int) -1; /* restore default handling */
50218334Speter		return AH_FALSE;
50318334Speter	} else {
50418334Speter		/* convert to system clocks */
50518334Speter		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
50618334Speter			AR_TIME_OUT_ACK, ath_hal_mac_clks(ah, us));
50718334Speter		ahp->ah_acktimeout = us;
50818334Speter		return AH_TRUE;
50918334Speter	}
51018334Speter}
51118334Speter
51218334Speteru_int
51318334Speterar5212GetAckTimeout(struct ath_hal *ah)
51418334Speter{
51518334Speter	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_ACK);
51618334Speter	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
51718334Speter}
51852284Sobrien
51918334Speteru_int
52090075Sobrienar5212GetAckCTSRate(struct ath_hal *ah)
52190075Sobrien{
52290075Sobrien	return ((AH5212(ah)->ah_staId1Defaults & AR_STA_ID1_ACKCTS_6MB) == 0);
52390075Sobrien}
52452284Sobrien
52590075SobrienHAL_BOOL
52652284Sobrienar5212SetAckCTSRate(struct ath_hal *ah, u_int high)
52718334Speter{
52818334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
52990075Sobrien
53018334Speter	if (high) {
53118334Speter		OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
53218334Speter		ahp->ah_staId1Defaults &= ~AR_STA_ID1_ACKCTS_6MB;
53318334Speter	} else {
53418334Speter		OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
53518334Speter		ahp->ah_staId1Defaults |= AR_STA_ID1_ACKCTS_6MB;
53618334Speter	}
53718334Speter	return AH_TRUE;
53818334Speter}
53918334Speter
54018334SpeterHAL_BOOL
54152284Sobrienar5212SetCTSTimeout(struct ath_hal *ah, u_int us)
54252284Sobrien{
54390075Sobrien	struct ath_hal_5212 *ahp = AH5212(ah);
54418334Speter
54518334Speter	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
54618334Speter		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad cts timeout %u\n",
54718334Speter		    __func__, us);
54850397Sobrien		ahp->ah_ctstimeout = (u_int) -1; /* restore default handling */
54950397Sobrien		return AH_FALSE;
55018334Speter	} else {
55118334Speter		/* convert to system clocks */
55218334Speter		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
55350397Sobrien			AR_TIME_OUT_CTS, ath_hal_mac_clks(ah, us));
55418334Speter		ahp->ah_ctstimeout = us;
55550397Sobrien		return AH_TRUE;
55650397Sobrien	}
55752284Sobrien}
55852284Sobrien
55990075Sobrienu_int
56090075Sobrienar5212GetCTSTimeout(struct ath_hal *ah)
56118334Speter{
56218334Speter	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_CTS);
56318334Speter	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
56418334Speter}
56550397Sobrien
56618334Speter/* Setup decompression for given key index */
56718334SpeterHAL_BOOL
56818334Speterar5212SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en)
56918334Speter{
57018334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
57118334Speter
57290075Sobrien        if (keyidx >= HAL_DECOMP_MASK_SIZE)
57318334Speter                return AH_FALSE;
57418334Speter        OS_REG_WRITE(ah, AR_DCM_A, keyidx);
57518334Speter        OS_REG_WRITE(ah, AR_DCM_D, en ? AR_DCM_D_EN : 0);
57618334Speter        ahp->ah_decompMask[keyidx] = en;
57718334Speter
57818334Speter        return AH_TRUE;
57918334Speter}
58018334Speter
58150397Sobrien/* Setup coverage class */
58250397Sobrienvoid
58318334Speterar5212SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now)
58418334Speter{
58518334Speter	uint32_t slot, timeout, eifs;
58618334Speter	u_int clkRate;
58718334Speter
58818334Speter	AH_PRIVATE(ah)->ah_coverageClass = coverageclass;
58918334Speter
59018334Speter	if (now) {
59118334Speter		if (AH_PRIVATE(ah)->ah_coverageClass == 0)
59290075Sobrien			return;
59318334Speter
59418334Speter		/* Don't apply coverage class to non A channels */
59550397Sobrien		if (!IS_CHAN_A(AH_PRIVATE(ah)->ah_curchan))
59690075Sobrien			return;
59790075Sobrien
59818334Speter		/* Get core clock rate */
59918334Speter		clkRate = ath_hal_mac_clks(ah, 1);
60090075Sobrien
60190075Sobrien		/* Compute EIFS */
60218334Speter		slot = coverageclass * 3 * clkRate;
60390075Sobrien		eifs = coverageclass * 6 * clkRate;
60490075Sobrien		if (IS_CHAN_HALF_RATE(AH_PRIVATE(ah)->ah_curchan)) {
60590075Sobrien			slot += IFS_SLOT_HALF_RATE;
60690075Sobrien			eifs += IFS_EIFS_HALF_RATE;
60790075Sobrien		} else if (IS_CHAN_QUARTER_RATE(AH_PRIVATE(ah)->ah_curchan)) {
60818334Speter			slot += IFS_SLOT_QUARTER_RATE;
60918334Speter			eifs += IFS_EIFS_QUARTER_RATE;
61050397Sobrien		} else { /* full rate */
61150397Sobrien			slot += IFS_SLOT_FULL_RATE;
61250397Sobrien			eifs += IFS_EIFS_FULL_RATE;
61350397Sobrien		}
61450397Sobrien
61550397Sobrien		/*
61650397Sobrien		 * Add additional time for air propagation for ACK and CTS
61750397Sobrien		 * timeouts. This value is in core clocks.
61850397Sobrien  		 */
61918334Speter		timeout = ACK_CTS_TIMEOUT_11A + (coverageclass * 3 * clkRate);
62018334Speter
62118334Speter		/*
62218334Speter		 * Write the values: slot, eifs, ack/cts timeouts.
62318334Speter		 */
62450397Sobrien		OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot);
62518334Speter		OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs);
62618334Speter		OS_REG_WRITE(ah, AR_TIME_OUT,
62718334Speter			  SM(timeout, AR_TIME_OUT_CTS)
62818334Speter			| SM(timeout, AR_TIME_OUT_ACK));
62918334Speter	}
63018334Speter}
63150397Sobrien
63250397Sobrienvoid
63318334Speterar5212SetPCUConfig(struct ath_hal *ah)
63418334Speter{
63590075Sobrien	ar5212SetOperatingMode(ah, AH_PRIVATE(ah)->ah_opmode);
63690075Sobrien}
63750397Sobrien
63890075Sobrien/*
63990075Sobrien * Return whether an external 32KHz crystal should be used
64050397Sobrien * to reduce power consumption when sleeping.  We do so if
64150397Sobrien * the crystal is present (obtained from EEPROM) and if we
64250397Sobrien * are not running as an AP and are configured to use it.
64350397Sobrien */
64450397SobrienHAL_BOOL
64518334Speterar5212Use32KHzclock(struct ath_hal *ah, HAL_OPMODE opmode)
64618334Speter{
64752284Sobrien	if (opmode != HAL_M_HOSTAP) {
64852284Sobrien		struct ath_hal_5212 *ahp = AH5212(ah);
64952284Sobrien		return ath_hal_eepromGetFlag(ah, AR_EEP_32KHZCRYSTAL) &&
65052284Sobrien		       (ahp->ah_enable32kHzClock == USE_32KHZ ||
65152284Sobrien		        ahp->ah_enable32kHzClock == AUTO_32KHZ);
65252284Sobrien	} else
65352284Sobrien		return AH_FALSE;
65452284Sobrien}
65552284Sobrien
65690075Sobrien/*
65790075Sobrien * If 32KHz clock exists, use it to lower power consumption during sleep
65890075Sobrien *
65990075Sobrien * Note: If clock is set to 32 KHz, delays on accessing certain
66052284Sobrien *       baseband registers (27-31, 124-127) are required.
66152284Sobrien */
66252284Sobrienvoid
66352284Sobrienar5212SetupClock(struct ath_hal *ah, HAL_OPMODE opmode)
66418334Speter{
66552284Sobrien	if (ar5212Use32KHzclock(ah, opmode)) {
66690075Sobrien		/*
66752284Sobrien		 * Enable clocks to be turned OFF in BB during sleep
66852284Sobrien		 * and also enable turning OFF 32MHz/40MHz Refclk
66952284Sobrien		 * from A2.
67018334Speter		 */
67118334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
67218334Speter		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
67318334Speter		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 0x14 : 0x18);
67418334Speter		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32, 1);
67518334Speter		OS_REG_WRITE(ah, AR_TSF_PARM, 61);  /* 32 KHz TSF incr */
67618334Speter		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 1);
67718334Speter
67818334Speter		if (IS_2413(ah) || IS_5413(ah) || IS_2417(ah)) {
67918334Speter			OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x26);
68018334Speter			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0d);
68118334Speter			OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x07);
68218334Speter			OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0x3f);
68350397Sobrien			/* # Set sleep clock rate to 32 KHz. */
68450397Sobrien			OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x2);
68552284Sobrien		} else {
68650397Sobrien			OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x0a);
68750397Sobrien			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0c);
68818334Speter			OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x03);
68990075Sobrien			OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0x20);
69018334Speter			OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x3);
69118334Speter		}
69218334Speter	} else {
69318334Speter		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x0);
69418334Speter		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0);
69518334Speter
69618334Speter		OS_REG_WRITE(ah, AR_TSF_PARM, 1);	/* 32MHz TSF inc */
69752284Sobrien
69818334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
69918334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x7f);
70018334Speter
70190075Sobrien		if (IS_2417(ah))
70218334Speter			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0a);
70318334Speter		else if (IS_HB63(ah))
70418334Speter			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x32);
70518334Speter		else
70618334Speter			OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e);
70750397Sobrien		OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x0c);
70890075Sobrien		OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0xff);
70990075Sobrien		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
71050397Sobrien		    IS_RAD5112_ANY(ah) || IS_5413(ah) || IS_2417(ah) ? 0x14 : 0x18);
71150397Sobrien		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32,
71250397Sobrien		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31);
71390075Sobrien	}
71490075Sobrien}
71590075Sobrien
71690075Sobrien/*
71790075Sobrien * If 32KHz clock exists, turn it off and turn back on the 32Mhz
71890075Sobrien */
71990075Sobrienvoid
72050397Sobrienar5212RestoreClock(struct ath_hal *ah, HAL_OPMODE opmode)
72118334Speter{
72218334Speter	if (ar5212Use32KHzclock(ah, opmode)) {
72318334Speter		/* # Set sleep clock rate back to 32 MHz. */
72418334Speter		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0);
72590075Sobrien		OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0);
72690075Sobrien
72790075Sobrien		OS_REG_WRITE(ah, AR_TSF_PARM, 1);	/* 32 MHz TSF incr */
72850397Sobrien		OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32,
72918334Speter		    IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31);
73018334Speter
73118334Speter		/*
73218334Speter		 * Restore BB registers to power-on defaults
73352284Sobrien		 */
73418334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f);
73518334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT,   0x7f);
73618334Speter		OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL,        0x0e);
73718334Speter		OS_REG_WRITE(ah, AR_PHY_M_SLEEP,           0x0c);
73890075Sobrien		OS_REG_WRITE(ah, AR_PHY_REFCLKDLY,         0xff);
73918334Speter		OS_REG_WRITE(ah, AR_PHY_REFCLKPD,
74018334Speter		    IS_RAD5112_ANY(ah) || IS_5413(ah) ?  0x14 : 0x18);
74150397Sobrien	}
74250397Sobrien}
74350397Sobrien
74450397Sobrien/*
74518334Speter * Adjust NF based on statistical values for 5GHz frequencies.
74618334Speter * Default method: this may be overridden by the rf backend.
74718334Speter */
74818334Speterint16_t
74918334Speterar5212GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c)
75018334Speter{
75118334Speter	static const struct {
75218334Speter		uint16_t freqLow;
75318334Speter		int16_t	  adjust;
75418334Speter	} adjustDef[] = {
75518334Speter		{ 5790,	11 },	/* NB: ordered high -> low */
75618334Speter		{ 5730, 10 },
75718334Speter		{ 5690,  9 },
75818334Speter		{ 5660,  8 },
75918334Speter		{ 5610,  7 },
76018334Speter		{ 5530,  5 },
76118334Speter		{ 5450,  4 },
76218334Speter		{ 5379,  2 },
76318334Speter		{ 5209,  0 },
76450397Sobrien		{ 3000,  1 },
76518334Speter		{    0,  0 },
76690075Sobrien	};
76790075Sobrien	int i;
76852284Sobrien
76950397Sobrien	for (i = 0; c->channel <= adjustDef[i].freqLow; i++)
77050397Sobrien		;
77150397Sobrien	return adjustDef[i].adjust;
77250397Sobrien}
77350397Sobrien
77450397SobrienHAL_STATUS
77590075Sobrienar5212GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
77690075Sobrien	uint32_t capability, uint32_t *result)
77790075Sobrien{
77890075Sobrien#define	MACVERSION(ah)	AH_PRIVATE(ah)->ah_macVersion
77950397Sobrien	struct ath_hal_5212 *ahp = AH5212(ah);
78050397Sobrien	const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
78150397Sobrien	const struct ar5212AniState *ani;
78218334Speter
78318334Speter	switch (type) {
78418334Speter	case HAL_CAP_CIPHER:		/* cipher handled in hardware */
78550397Sobrien		switch (capability) {
78618334Speter		case HAL_CIPHER_AES_CCM:
78718334Speter			return pCap->halCipherAesCcmSupport ?
78818334Speter				HAL_OK : HAL_ENOTSUPP;
78918334Speter		case HAL_CIPHER_AES_OCB:
79018334Speter		case HAL_CIPHER_TKIP:
79118334Speter		case HAL_CIPHER_WEP:
79218334Speter		case HAL_CIPHER_MIC:
79390075Sobrien		case HAL_CIPHER_CLR:
79418334Speter			return HAL_OK;
79518334Speter		default:
79618334Speter			return HAL_ENOTSUPP;
79718334Speter		}
79818334Speter	case HAL_CAP_TKIP_MIC:		/* handle TKIP MIC in hardware */
79918334Speter		switch (capability) {
80090075Sobrien		case 0:			/* hardware capability */
80190075Sobrien			return HAL_OK;
80290075Sobrien		case 1:
80390075Sobrien			return (ahp->ah_staId1Defaults &
80490075Sobrien			    AR_STA_ID1_CRPT_MIC_ENABLE) ?  HAL_OK : HAL_ENXIO;
80590075Sobrien		}
80690075Sobrien		return HAL_EINVAL;
80790075Sobrien	case HAL_CAP_TKIP_SPLIT:	/* hardware TKIP uses split keys */
80890075Sobrien		switch (capability) {
80990075Sobrien		case 0:			/* hardware capability */
81090075Sobrien			return pCap->halTkipMicTxRxKeySupport ?
81190075Sobrien				HAL_ENXIO : HAL_OK;
81290075Sobrien		case 1:			/* current setting */
81390075Sobrien			return (ahp->ah_miscMode &
81490075Sobrien			    AR_MISC_MODE_MIC_NEW_LOC_ENABLE) ? HAL_ENXIO : HAL_OK;
81590075Sobrien		}
81690075Sobrien		return HAL_EINVAL;
81790075Sobrien	case HAL_CAP_WME_TKIPMIC:	/* hardware can do TKIP MIC w/ WMM */
81890075Sobrien		/* XXX move to capability bit */
81990075Sobrien		return MACVERSION(ah) > AR_SREV_VERSION_VENICE ||
82090075Sobrien		    (MACVERSION(ah) == AR_SREV_VERSION_VENICE &&
82190075Sobrien		     AH_PRIVATE(ah)->ah_macRev >= 8) ? HAL_OK : HAL_ENOTSUPP;
82290075Sobrien	case HAL_CAP_DIVERSITY:		/* hardware supports fast diversity */
82390075Sobrien		switch (capability) {
82490075Sobrien		case 0:			/* hardware capability */
82590075Sobrien			return HAL_OK;
82690075Sobrien		case 1:			/* current setting */
82790075Sobrien			return ahp->ah_diversity ? HAL_OK : HAL_ENXIO;
82890075Sobrien		}
82990075Sobrien		return HAL_EINVAL;
83090075Sobrien	case HAL_CAP_DIAG:
83190075Sobrien		*result = AH_PRIVATE(ah)->ah_diagreg;
83290075Sobrien		return HAL_OK;
83390075Sobrien	case HAL_CAP_TPC:
83490075Sobrien		switch (capability) {
83590075Sobrien		case 0:			/* hardware capability */
83690075Sobrien			return HAL_OK;
83790075Sobrien		case 1:
83890075Sobrien			return ahp->ah_tpcEnabled ? HAL_OK : HAL_ENXIO;
83990075Sobrien		}
84090075Sobrien		return HAL_OK;
84190075Sobrien	case HAL_CAP_PHYDIAG:		/* radar pulse detection capability */
84290075Sobrien		switch (capability) {
84390075Sobrien		case HAL_CAP_RADAR:
84490075Sobrien			return ath_hal_eepromGetFlag(ah, AR_EEP_AMODE) ?
84590075Sobrien			    HAL_OK: HAL_ENXIO;
84690075Sobrien		case HAL_CAP_AR:
84790075Sobrien			return (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) ||
84890075Sobrien			    ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) ?
84990075Sobrien			       HAL_OK: HAL_ENXIO;
85090075Sobrien		}
85190075Sobrien		return HAL_ENXIO;
85290075Sobrien	case HAL_CAP_MCAST_KEYSRCH:	/* multicast frame keycache search */
85390075Sobrien		switch (capability) {
85490075Sobrien		case 0:			/* hardware capability */
85590075Sobrien			return HAL_OK;
85690075Sobrien		case 1:
85790075Sobrien			return (ahp->ah_staId1Defaults &
85890075Sobrien			    AR_STA_ID1_MCAST_KSRCH) ? HAL_OK : HAL_ENXIO;
85990075Sobrien		}
86090075Sobrien		return HAL_EINVAL;
86190075Sobrien	case HAL_CAP_TSF_ADJUST:	/* hardware has beacon tsf adjust */
86290075Sobrien		switch (capability) {
86390075Sobrien		case 0:			/* hardware capability */
86490075Sobrien			return pCap->halTsfAddSupport ? HAL_OK : HAL_ENOTSUPP;
86590075Sobrien		case 1:
86690075Sobrien			return (ahp->ah_miscMode & AR_MISC_MODE_TX_ADD_TSF) ?
86790075Sobrien				HAL_OK : HAL_ENXIO;
86890075Sobrien		}
86990075Sobrien		return HAL_EINVAL;
87090075Sobrien	case HAL_CAP_TPC_ACK:
87190075Sobrien		*result = MS(ahp->ah_macTPC, AR_TPC_ACK);
87290075Sobrien		return HAL_OK;
87390075Sobrien	case HAL_CAP_TPC_CTS:
87490075Sobrien		*result = MS(ahp->ah_macTPC, AR_TPC_CTS);
87590075Sobrien		return HAL_OK;
87690075Sobrien	case HAL_CAP_INTMIT:		/* interference mitigation */
87790075Sobrien		switch (capability) {
87890075Sobrien		case 0:			/* hardware capability */
87990075Sobrien			return HAL_OK;
88090075Sobrien		case 1:
88190075Sobrien			return (ahp->ah_procPhyErr & HAL_ANI_ENA) ?
88290075Sobrien				HAL_OK : HAL_ENXIO;
88390075Sobrien		case 2:			/* HAL_ANI_NOISE_IMMUNITY_LEVEL */
88490075Sobrien		case 3:			/* HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION */
88590075Sobrien		case 4:			/* HAL_ANI_CCK_WEAK_SIGNAL_THR */
88690075Sobrien		case 5:			/* HAL_ANI_FIRSTEP_LEVEL */
88790075Sobrien		case 6:			/* HAL_ANI_SPUR_IMMUNITY_LEVEL */
88890075Sobrien			ani = ar5212AniGetCurrentState(ah);
88990075Sobrien			if (ani == AH_NULL)
89090075Sobrien				return HAL_ENXIO;
89190075Sobrien			switch (capability) {
89290075Sobrien			case 2:	*result = ani->noiseImmunityLevel; break;
89390075Sobrien			case 3: *result = !ani->ofdmWeakSigDetectOff; break;
89490075Sobrien			case 4: *result = ani->cckWeakSigThreshold; break;
89590075Sobrien			case 5: *result = ani->firstepLevel; break;
89690075Sobrien			case 6: *result = ani->spurImmunityLevel; break;
89790075Sobrien			}
89890075Sobrien			return HAL_OK;
89990075Sobrien		}
90090075Sobrien		return HAL_EINVAL;
90190075Sobrien	default:
90290075Sobrien		return ath_hal_getcapability(ah, type, capability, result);
90390075Sobrien	}
90490075Sobrien#undef MACVERSION
90590075Sobrien}
90690075Sobrien
90790075SobrienHAL_BOOL
90890075Sobrienar5212SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
90990075Sobrien	uint32_t capability, uint32_t setting, HAL_STATUS *status)
91090075Sobrien{
91190075Sobrien#define	N(a)	(sizeof(a)/sizeof(a[0]))
91290075Sobrien	struct ath_hal_5212 *ahp = AH5212(ah);
91390075Sobrien	const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
91490075Sobrien	uint32_t v;
91590075Sobrien
91690075Sobrien	switch (type) {
91790075Sobrien	case HAL_CAP_TKIP_MIC:		/* handle TKIP MIC in hardware */
91890075Sobrien		if (setting)
91990075Sobrien			ahp->ah_staId1Defaults |= AR_STA_ID1_CRPT_MIC_ENABLE;
92090075Sobrien		else
92190075Sobrien			ahp->ah_staId1Defaults &= ~AR_STA_ID1_CRPT_MIC_ENABLE;
92290075Sobrien		return AH_TRUE;
92390075Sobrien	case HAL_CAP_TKIP_SPLIT:	/* hardware TKIP uses split keys */
92490075Sobrien		if (!pCap->halTkipMicTxRxKeySupport)
92590075Sobrien			return AH_FALSE;
92690075Sobrien		/* NB: true =>'s use split key cache layout */
92790075Sobrien		if (setting)
92890075Sobrien			ahp->ah_miscMode &= ~AR_MISC_MODE_MIC_NEW_LOC_ENABLE;
92990075Sobrien		else
93090075Sobrien			ahp->ah_miscMode |= AR_MISC_MODE_MIC_NEW_LOC_ENABLE;
93190075Sobrien		/* NB: write here so keys can be setup w/o a reset */
93290075Sobrien		OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode);
93390075Sobrien		return AH_TRUE;
93490075Sobrien	case HAL_CAP_DIVERSITY:
93590075Sobrien		if (ahp->ah_phyPowerOn) {
93690075Sobrien			v = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
93790075Sobrien			if (setting)
93890075Sobrien				v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
93918334Speter			else
94018334Speter				v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
94118334Speter			OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
94218334Speter		}
94350397Sobrien		ahp->ah_diversity = (setting != 0);
94418334Speter		return AH_TRUE;
94550397Sobrien	case HAL_CAP_DIAG:		/* hardware diagnostic support */
94650397Sobrien		/*
94750397Sobrien		 * NB: could split this up into virtual capabilities,
94850397Sobrien		 *     (e.g. 1 => ACK, 2 => CTS, etc.) but it hardly
94950397Sobrien		 *     seems worth the additional complexity.
95050397Sobrien		 */
95190075Sobrien		AH_PRIVATE(ah)->ah_diagreg = setting;
95250397Sobrien		OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
95350397Sobrien		return AH_TRUE;
95450397Sobrien	case HAL_CAP_TPC:
95550397Sobrien		ahp->ah_tpcEnabled = (setting != 0);
95618334Speter		return AH_TRUE;
95718334Speter	case HAL_CAP_MCAST_KEYSRCH:	/* multicast frame keycache search */
95818334Speter		if (setting)
95918334Speter			ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH;
96050397Sobrien		else
96150397Sobrien			ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH;
96250397Sobrien		return AH_TRUE;
96350397Sobrien	case HAL_CAP_TPC_ACK:
96450397Sobrien	case HAL_CAP_TPC_CTS:
96550397Sobrien		setting += ahp->ah_txPowerIndexOffset;
96650397Sobrien		if (setting > 63)
96750397Sobrien			setting = 63;
96850397Sobrien		if (type == HAL_CAP_TPC_ACK) {
96990075Sobrien			ahp->ah_macTPC &= AR_TPC_ACK;
97050397Sobrien			ahp->ah_macTPC |= MS(setting, AR_TPC_ACK);
97150397Sobrien		} else {
97250397Sobrien			ahp->ah_macTPC &= AR_TPC_CTS;
97350397Sobrien			ahp->ah_macTPC |= MS(setting, AR_TPC_CTS);
97450397Sobrien		}
97518334Speter		OS_REG_WRITE(ah, AR_TPC, ahp->ah_macTPC);
97618334Speter		return AH_TRUE;
97750397Sobrien	case HAL_CAP_INTMIT: {		/* interference mitigation */
97818334Speter		static const HAL_ANI_CMD cmds[] = {
97918334Speter			HAL_ANI_PRESENT,
98050397Sobrien			HAL_ANI_MODE,
98118334Speter			HAL_ANI_NOISE_IMMUNITY_LEVEL,
98218334Speter			HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
98318334Speter			HAL_ANI_CCK_WEAK_SIGNAL_THR,
98418334Speter			HAL_ANI_FIRSTEP_LEVEL,
98518334Speter			HAL_ANI_SPUR_IMMUNITY_LEVEL,
98618334Speter		};
98718334Speter		return capability < N(cmds) ?
98818334Speter			ar5212AniControl(ah, cmds[capability], setting) :
98918334Speter			AH_FALSE;
99018334Speter	}
99118334Speter	case HAL_CAP_TSF_ADJUST:	/* hardware has beacon tsf adjust */
99218334Speter		if (pCap->halTsfAddSupport) {
99318334Speter			if (setting)
99418334Speter				ahp->ah_miscMode |= AR_MISC_MODE_TX_ADD_TSF;
99518334Speter			else
99618334Speter				ahp->ah_miscMode &= ~AR_MISC_MODE_TX_ADD_TSF;
99718334Speter			return AH_TRUE;
99818334Speter		}
99918334Speter		/* fall thru... */
100018334Speter	default:
100118334Speter		return ath_hal_setcapability(ah, type, capability,
100218334Speter				setting, status);
100318334Speter	}
100418334Speter#undef N
100518334Speter}
100618334Speter
100750397SobrienHAL_BOOL
100818334Speterar5212GetDiagState(struct ath_hal *ah, int request,
100918334Speter	const void *args, uint32_t argsize,
101018334Speter	void **result, uint32_t *resultsize)
101150397Sobrien{
101218334Speter	struct ath_hal_5212 *ahp = AH5212(ah);
101318334Speter
101418334Speter	(void) ahp;
101518334Speter	if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize))
101618334Speter		return AH_TRUE;
101718334Speter	switch (request) {
101818334Speter	case HAL_DIAG_EEPROM:
101918334Speter	case HAL_DIAG_EEPROM_EXP_11A:
102018334Speter	case HAL_DIAG_EEPROM_EXP_11B:
102118334Speter	case HAL_DIAG_EEPROM_EXP_11G:
102218334Speter	case HAL_DIAG_RFGAIN:
102318334Speter		return ath_hal_eepromDiag(ah, request,
102418334Speter		    args, argsize, result, resultsize);
102518334Speter	case HAL_DIAG_RFGAIN_CURSTEP:
102618334Speter		*result = __DECONST(void *, ahp->ah_gainValues.currStep);
102718334Speter		*resultsize = (*result == AH_NULL) ?
102818334Speter			0 : sizeof(GAIN_OPTIMIZATION_STEP);
102918334Speter		return AH_TRUE;
103018334Speter	case HAL_DIAG_PCDAC:
103118334Speter		*result = ahp->ah_pcdacTable;
103252284Sobrien		*resultsize = ahp->ah_pcdacTableSize;
103318334Speter		return AH_TRUE;
103418334Speter	case HAL_DIAG_TXRATES:
103518334Speter		*result = &ahp->ah_ratesArray[0];
103618334Speter		*resultsize = sizeof(ahp->ah_ratesArray);
103718334Speter		return AH_TRUE;
103850397Sobrien	case HAL_DIAG_ANI_CURRENT:
103950397Sobrien		*result = ar5212AniGetCurrentState(ah);
104018334Speter		*resultsize = (*result == AH_NULL) ?
104118334Speter			0 : sizeof(struct ar5212AniState);
104218334Speter		return AH_TRUE;
104318334Speter	case HAL_DIAG_ANI_STATS:
104418334Speter		*result = ar5212AniGetCurrentStats(ah);
104518334Speter		*resultsize = (*result == AH_NULL) ?
104618334Speter			0 : sizeof(struct ar5212Stats);
104718334Speter		return AH_TRUE;
104818334Speter	case HAL_DIAG_ANI_CMD:
104918334Speter		if (argsize != 2*sizeof(uint32_t))
105018334Speter			return AH_FALSE;
105118334Speter		ar5212AniControl(ah, ((const uint32_t *)args)[0],
105218334Speter			((const uint32_t *)args)[1]);
105318334Speter		return AH_TRUE;
105452284Sobrien	case HAL_DIAG_ANI_PARAMS:
105518334Speter		/*
105618334Speter		 * NB: We assume struct ar5212AniParams is identical
105752284Sobrien		 * to HAL_ANI_PARAMS; if they diverge then we'll need
105852284Sobrien		 * to handle it here
105952284Sobrien		 */
106090075Sobrien		if (argsize == 0 && args == AH_NULL) {
106152284Sobrien			struct ar5212AniState *aniState =
106252284Sobrien			    ar5212AniGetCurrentState(ah);
106318334Speter			if (aniState == AH_NULL)
106418334Speter				return AH_FALSE;
106518334Speter			*result = __DECONST(void *, aniState->params);
106618334Speter			*resultsize = sizeof(struct ar5212AniParams);
106790075Sobrien			return AH_TRUE;
106890075Sobrien		} else {
106990075Sobrien			if (argsize != sizeof(struct ar5212AniParams))
107018334Speter				return AH_FALSE;
107118334Speter			return ar5212AniSetParams(ah, args, args);
107218334Speter		}
107318334Speter	}
107452284Sobrien	return AH_FALSE;
107518334Speter}
107650397Sobrien
107718334Speter/*
107818334Speter * Check whether there's an in-progress NF completion.
107918334Speter *
108018334Speter * Returns AH_TRUE if there's a in-progress NF calibration, AH_FALSE
108118334Speter * otherwise.
108218334Speter */
108318334SpeterHAL_BOOL
108418334Speterar5212IsNFCalInProgress(struct ath_hal *ah)
108518334Speter{
108618334Speter	if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)
108718334Speter		return AH_TRUE;
108818334Speter	return AH_FALSE;
108918334Speter}
109018334Speter
109118334Speter/*
109218334Speter * Wait for an in-progress NF calibration to complete.
109318334Speter *
109418334Speter * The completion function waits "i" times 10uS.
109550397Sobrien * It returns AH_TRUE if the NF calibration completed (or was never
109650397Sobrien * in progress); AH_FALSE if it was still in progress after "i" checks.
109750397Sobrien */
109818334SpeterHAL_BOOL
109918334Speterar5212WaitNFCalComplete(struct ath_hal *ah, int i)
110018334Speter{
110118334Speter	int j;
110218334Speter
110318334Speter	if (i <= 0)
110418334Speter		i = 1;	/* it should run at least once */
110550397Sobrien	for (j = 0; j < i; i++) {
110618334Speter		if (! ar5212IsNFCalInProgress(ah))
110750397Sobrien			return AH_TRUE;
110818334Speter		OS_DELAY(10);
110918334Speter	}
111050397Sobrien	return AH_FALSE;
111118334Speter}
111218334Speter