164921Smarcel/*-
264921Smarcel * SPDX-License-Identifier: ISC
364921Smarcel *
464921Smarcel * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
564921Smarcel * Copyright (c) 2002-2008 Atheros Communications, Inc.
664921Smarcel *
764921Smarcel * Permission to use, copy, modify, and/or distribute this software for any
864921Smarcel * purpose with or without fee is hereby granted, provided that the above
964921Smarcel * copyright notice and this permission notice appear in all copies.
1064921Smarcel *
1164921Smarcel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1264921Smarcel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1364921Smarcel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1464921Smarcel * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1565067Smarcel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1664921Smarcel * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1764921Smarcel * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1864921Smarcel *
1964921Smarcel * $FreeBSD: releng/12.0/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 326695 2017-12-08 15:57:29Z pfg $
2064921Smarcel */
2164921Smarcel#include "opt_ah.h"
2264921Smarcel
2364921Smarcel#include "ah.h"
2464921Smarcel#include "ah_internal.h"
2564921Smarcel#include "ah_devid.h"
2664921Smarcel#include "ah_desc.h"                    /* NB: for HAL_PHYERR* */
2764921Smarcel
2864921Smarcel#include "ar5416/ar5416.h"
2964921Smarcel#include "ar5416/ar5416reg.h"
3064921Smarcel#include "ar5416/ar5416phy.h"
3164921Smarcel
3276166Smarkm#include "ah_eeprom_v14.h"	/* for owl_get_ntxchains() */
3384811Sjhb
3464921Smarcel/*
3576166Smarkm * Return the wireless modes (a,b,g,n,t) supported by hardware.
3664921Smarcel *
3776166Smarkm * This value is what is actually supported by the hardware
3876166Smarkm * and is unaffected by regulatory/country code settings.
39104984Sbde *
40102814Siedowse */
4164921Smarcelu_int
4264921Smarcelar5416GetWirelessModes(struct ath_hal *ah)
4364921Smarcel{
4464921Smarcel	u_int mode;
4564921Smarcel	struct ath_hal_private *ahpriv = AH_PRIVATE(ah);
4664921Smarcel	HAL_CAPABILITIES *pCap = &ahpriv->ah_caps;
4764921Smarcel
4864921Smarcel	mode = ar5212GetWirelessModes(ah);
4967238Sgallatin
5067238Sgallatin	/* Only enable HT modes if the NIC supports HT */
5167238Sgallatin	if (pCap->halHTSupport == AH_TRUE && (mode & HAL_MODE_11A))
5267238Sgallatin		mode |= HAL_MODE_11NA_HT20
5364921Smarcel		     |  HAL_MODE_11NA_HT40PLUS
5468583Smarcel		     |  HAL_MODE_11NA_HT40MINUS
5564921Smarcel		     ;
5664921Smarcel	if (pCap->halHTSupport == AH_TRUE && (mode & HAL_MODE_11G))
5764921Smarcel		mode |= HAL_MODE_11NG_HT20
5864921Smarcel		     |  HAL_MODE_11NG_HT40PLUS
5983221Smarcel		     |  HAL_MODE_11NG_HT40MINUS
6083221Smarcel		     ;
6183221Smarcel	return mode;
6283221Smarcel}
6383221Smarcel
6483221Smarcel/*
6583221Smarcel * Change the LED blinking pattern to correspond to the connectivity
6683221Smarcel */
6783221Smarcelvoid
6883221Smarcelar5416SetLedState(struct ath_hal *ah, HAL_LED_STATE state)
6964921Smarcel{
7064921Smarcel	static const uint32_t ledbits[8] = {
7183221Smarcel		AR_MAC_LED_ASSOC_NONE,		/* HAL_LED_INIT */
7283221Smarcel		AR_MAC_LED_ASSOC_PEND,		/* HAL_LED_SCAN */
7383221Smarcel		AR_MAC_LED_ASSOC_PEND,		/* HAL_LED_AUTH */
7483221Smarcel		AR_MAC_LED_ASSOC_ACTIVE,	/* HAL_LED_ASSOC*/
7583221Smarcel		AR_MAC_LED_ASSOC_ACTIVE,	/* HAL_LED_RUN */
7683221Smarcel		AR_MAC_LED_ASSOC_NONE,
7764921Smarcel		AR_MAC_LED_ASSOC_NONE,
7864921Smarcel		AR_MAC_LED_ASSOC_NONE,
7964921Smarcel	};
8067051Sgallatin
8167051Sgallatin	if (AR_SREV_HOWL(ah))
8267051Sgallatin		return;
8367051Sgallatin
8467051Sgallatin	/*
8567051Sgallatin	 * Set the blink operating mode.
8667051Sgallatin	 */
8767051Sgallatin	OS_REG_RMW_FIELD(ah, AR_MAC_LED,
8867051Sgallatin	    AR_MAC_LED_ASSOC, ledbits[state & 0x7]);
8967051Sgallatin
9067051Sgallatin	/* XXX Blink slow mode? */
9167051Sgallatin	/* XXX Blink threshold? */
9267051Sgallatin	/* XXX Blink sleep hystersis? */
9367051Sgallatin
9467051Sgallatin	/*
9567051Sgallatin	 * Set the LED blink configuration to be proportional
9667051Sgallatin	 * to the current TX and RX filter bytes.  (Ie, RX'ed
9767051Sgallatin	 * frames that don't match the filter are ignored.)
9867051Sgallatin	 * This means that higher TX/RX throughput will result
9967051Sgallatin	 * in the blink rate increasing.
10067051Sgallatin	 */
10167051Sgallatin	OS_REG_RMW_FIELD(ah, AR_MAC_LED, AR_MAC_LED_MODE,
10267051Sgallatin	    AR_MAC_LED_MODE_PROP);
10367051Sgallatin}
10483366Sjulian
10564921Smarcel/*
10664921Smarcel * Get the current hardware tsf for stamlme
10764921Smarcel */
10864921Smarceluint64_t
10964921Smarcelar5416GetTsf64(struct ath_hal *ah)
11083366Sjulian{
11164921Smarcel	uint32_t low1, low2, u32;
11264921Smarcel
11372543Sjlemon	/* sync multi-word read */
11472543Sjlemon	low1 = OS_REG_READ(ah, AR_TSF_L32);
11564921Smarcel	u32 = OS_REG_READ(ah, AR_TSF_U32);
11664921Smarcel	low2 = OS_REG_READ(ah, AR_TSF_L32);
11764921Smarcel	if (low2 < low1) {	/* roll over */
11864921Smarcel		/*
11964921Smarcel		 * If we are not preempted this will work.  If we are
12083366Sjulian		 * then we re-reading AR_TSF_U32 does no good as the
12164921Smarcel		 * low bits will be meaningless.  Likewise reading
12264921Smarcel		 * L32, U32, U32, then comparing the last two reads
12383221Smarcel		 * to check for rollover doesn't help if preempted--so
12483221Smarcel		 * we take this approach as it costs one less PCI read
12583221Smarcel		 * which can be noticeable when doing things like
12683221Smarcel		 * timestamping packets in monitor mode.
12783221Smarcel		 */
12864921Smarcel		u32++;
12983366Sjulian	}
13064921Smarcel	return (((uint64_t) u32) << 32) | ((uint64_t) low2);
13183221Smarcel}
13283221Smarcel
13383221Smarcel/*
13483221Smarcel * Update the TSF.
13583221Smarcel *
13683221Smarcel * The full TSF is only updated once the upper 32 bits have
13783221Smarcel * been written.  Writing only the lower 32 bits of the TSF
13883221Smarcel * will not actually correctly update the TSF.
13983366Sjulian *
14064921Smarcel * The #if 0'ed code is to check whether the previous TSF
14183221Smarcel * reset or write has completed before writing to the
14283221Smarcel * TSF.  Strictly speaking, it should be also checked before
14364921Smarcel * reading the TSF as the write/reset may not have completed.
14483221Smarcel */
14583221Smarcelvoid
14683221Smarcelar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64)
14783366Sjulian{
14883221Smarcel	/* XXX check if this is correct! */
14983221Smarcel#if 0
15083221Smarcel	int i;
15183221Smarcel	uint32_t v;
15283221Smarcel
15383221Smarcel	for (i = 0; i < 10; i++) {
15483221Smarcel		v = OS_REG_READ(ah, AR_SLP32_MODE);
15583221Smarcel		if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0)
156111797Sdes			break;
15783221Smarcel		OS_DELAY(10);
15883221Smarcel	}
15983366Sjulian	if (i == 10)
16083221Smarcel		ath_hal_printf(ah, "%s: couldn't slew things right!\n", __func__);
16183221Smarcel#endif
16283221Smarcel
16383221Smarcel	OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff);
16483221Smarcel	OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff);
16583221Smarcel}
16683221Smarcel
16783221Smarcel/*
16883366Sjulian * Reset the current hardware tsf for stamlme.
16983221Smarcel */
17083221Smarcelvoid
17183221Smarcelar5416ResetTsf(struct ath_hal *ah)
17283221Smarcel{
17383221Smarcel	uint32_t v;
17483221Smarcel	int i;
17583221Smarcel
17683221Smarcel	for (i = 0; i < 10; i++) {
17783221Smarcel		v = OS_REG_READ(ah, AR_SLP32_MODE);
17883221Smarcel		if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0)
17983221Smarcel			break;
18083221Smarcel		OS_DELAY(10);
18183221Smarcel	}
182111797Sdes	OS_REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
18383221Smarcel}
18483221Smarcel
18583221Smarceluint32_t
18683221Smarcelar5416GetCurRssi(struct ath_hal *ah)
18783221Smarcel{
18883221Smarcel	if (AR_SREV_OWL(ah))
18983221Smarcel		return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff);
19083221Smarcel	return (OS_REG_READ(ah, AR9130_PHY_CURRENT_RSSI) & 0xff);
19183366Sjulian}
19283221Smarcel
19383221SmarcelHAL_BOOL
19483221Smarcelar5416SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
19583221Smarcel{
19683221Smarcel	return AH_TRUE;
19783221Smarcel}
19883366Sjulian
19983221Smarcel/* Setup decompression for given key index */
20083221SmarcelHAL_BOOL
20183221Smarcelar5416SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en)
20283221Smarcel{
20383221Smarcel	return AH_TRUE;
20483221Smarcel}
20583221Smarcel
20683366Sjulian/* Setup coverage class */
20783221Smarcelvoid
20883221Smarcelar5416SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now)
20983221Smarcel{
21083221Smarcel
21183221Smarcel	ar5212SetCoverageClass(ah, coverageclass, now);
21283221Smarcel}
21383221Smarcel
21483221Smarcel/*
21583366Sjulian * Return the busy for rx_frame, rx_clear, and tx_frame
21683221Smarcel */
21783221SmarcelHAL_BOOL
21883221Smarcelar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
21983221Smarcel{
22083221Smarcel	struct ath_hal_5416 *ahp = AH5416(ah);
22183366Sjulian	u_int32_t good = AH_TRUE;
22283221Smarcel
22383221Smarcel	/* XXX freeze/unfreeze mib counters */
22483221Smarcel	uint32_t rc = OS_REG_READ(ah, AR_RCCNT);
22583221Smarcel	uint32_t ec = OS_REG_READ(ah, AR_EXTRCCNT);
22683221Smarcel	uint32_t rf = OS_REG_READ(ah, AR_RFCNT);
22783221Smarcel	uint32_t tf = OS_REG_READ(ah, AR_TFCNT);
22883221Smarcel	uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */
22983366Sjulian
23083221Smarcel	if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) {
23183221Smarcel		/*
23283221Smarcel		 * Cycle counter wrap (or initial call); it's not possible
23383221Smarcel		 * to accurately calculate a value because the registers
23483221Smarcel		 * right shift rather than wrap--so punt and return 0.
23583221Smarcel		 */
23683221Smarcel		HALDEBUG(ah, HAL_DEBUG_ANY,
23783366Sjulian			    "%s: cycle counter wrap. ExtBusy = 0\n", __func__);
23883221Smarcel			good = AH_FALSE;
23983221Smarcel	} else {
24083221Smarcel		hsample->cycle_count = cc - ahp->ah_cycleCount;
24183221Smarcel		hsample->chan_busy = rc - ahp->ah_ctlBusy;
24283221Smarcel		hsample->ext_chan_busy = ec - ahp->ah_extBusy;
24383221Smarcel		hsample->rx_busy = rf - ahp->ah_rxBusy;
24464921Smarcel		hsample->tx_busy = tf - ahp->ah_txBusy;
24564921Smarcel	}
24664921Smarcel
24783366Sjulian	/*
24864921Smarcel	 * Keep a copy of the MIB results so the next sample has something
24983221Smarcel	 * to work from.
25083221Smarcel	 */
25164921Smarcel	ahp->ah_cycleCount = cc;
25264921Smarcel	ahp->ah_rxBusy = rf;
25383221Smarcel	ahp->ah_ctlBusy = rc;
25483221Smarcel	ahp->ah_txBusy = tf;
25591437Speter	ahp->ah_extBusy = ec;
25664921Smarcel
25764921Smarcel	return (good);
258111797Sdes}
25964921Smarcel
26064921Smarcel/*
26164921Smarcel * Setup the TX/RX chainmasks - this needs to be done before a call
26264921Smarcel * to the reset method as it doesn't update the hardware.
26364921Smarcel */
26464921Smarcelvoid
26564921Smarcelar5416SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask,
26664921Smarcel    uint32_t rx_chainmask)
26783366Sjulian{
26864921Smarcel	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
26964921Smarcel
27064921Smarcel	AH5416(ah)->ah_tx_chainmask = tx_chainmask & pCap->halTxChainMask;
27183366Sjulian	AH5416(ah)->ah_rx_chainmask = rx_chainmask & pCap->halRxChainMask;
27264921Smarcel}
27364921Smarcel
27464921Smarcel/*
27564921Smarcel * Return approximation of extension channel busy over an time interval
27672543Sjlemon * 0% (clear) -> 100% (busy)
27772543Sjlemon *
27864921Smarcel * XXX TODO: update this to correctly sample all the counters,
27964921Smarcel *           rather than a subset of it.
28083366Sjulian */
28164921Smarceluint32_t
28264921Smarcelar5416Get11nExtBusy(struct ath_hal *ah)
28383366Sjulian{
28483366Sjulian    struct ath_hal_5416 *ahp = AH5416(ah);
28564921Smarcel    uint32_t busy; /* percentage */
28664921Smarcel    uint32_t cycleCount, ctlBusy, extBusy;
28764921Smarcel
28864921Smarcel    ctlBusy = OS_REG_READ(ah, AR_RCCNT);
28983366Sjulian    extBusy = OS_REG_READ(ah, AR_EXTRCCNT);
29064921Smarcel    cycleCount = OS_REG_READ(ah, AR_CCCNT);
29164921Smarcel
29264921Smarcel    if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cycleCount) {
29364921Smarcel        /*
29472543Sjlemon         * Cycle counter wrap (or initial call); it's not possible
29572543Sjlemon         * to accurately calculate a value because the registers
29664921Smarcel         * right shift rather than wrap--so punt and return 0.
29764921Smarcel         */
29883366Sjulian        busy = 0;
29964921Smarcel        HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cycle counter wrap. ExtBusy = 0\n",
30064921Smarcel	    __func__);
30183366Sjulian
30283366Sjulian    } else {
30364921Smarcel        uint32_t cycleDelta = cycleCount - ahp->ah_cycleCount;
30464921Smarcel        uint32_t ctlBusyDelta = ctlBusy - ahp->ah_ctlBusy;
30564921Smarcel        uint32_t extBusyDelta = extBusy - ahp->ah_extBusy;
30664921Smarcel        uint32_t ctlClearDelta = 0;
30764921Smarcel
30864921Smarcel        /* Compute control channel rxclear.
30964921Smarcel         * The cycle delta may be less than the control channel delta.
31064921Smarcel         * This could be solved by freezing the timers (or an atomic read,
31164921Smarcel         * if one was available). Checking for the condition should be
31264921Smarcel         * sufficient.
31383366Sjulian         */
31464921Smarcel        if (cycleDelta > ctlBusyDelta) {
31573856Sjhb            ctlClearDelta = cycleDelta - ctlBusyDelta;
31664921Smarcel        }
31764921Smarcel
31864921Smarcel        /* Compute ratio of extension channel busy to control channel clear
31964921Smarcel         * as an approximation to extension channel cleanliness.
32072543Sjlemon         *
32172543Sjlemon         * According to the hardware folks, ext rxclear is undefined
32272543Sjlemon         * if the ctrl rxclear is de-asserted (i.e. busy)
32372543Sjlemon         */
32472543Sjlemon        if (ctlClearDelta) {
32572543Sjlemon            busy = (extBusyDelta * 100) / ctlClearDelta;
32664921Smarcel        } else {
32764921Smarcel            busy = 100;
32864921Smarcel        }
32964921Smarcel        if (busy > 100) {
33064921Smarcel            busy = 100;
33164921Smarcel        }
33264921Smarcel#if 0
33364921Smarcel        HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cycleDelta 0x%x, ctlBusyDelta 0x%x, "
33464921Smarcel             "extBusyDelta 0x%x, ctlClearDelta 0x%x, "
33564921Smarcel             "busy %d\n",
33664921Smarcel              __func__, cycleDelta, ctlBusyDelta, extBusyDelta, ctlClearDelta, busy);
33764921Smarcel#endif
33864921Smarcel    }
33964921Smarcel
34064921Smarcel    ahp->ah_cycleCount = cycleCount;
34164921Smarcel    ahp->ah_ctlBusy = ctlBusy;
34264921Smarcel    ahp->ah_extBusy = extBusy;
34364921Smarcel
34464921Smarcel    return busy;
34583221Smarcel}
346104354Sscottl
34783221Smarcel/*
34883366Sjulian * Configure 20/40 operation
34983366Sjulian *
35064921Smarcel * 20/40 = joint rx clear (control and extension)
35183221Smarcel * 20    = rx clear (control)
35283221Smarcel *
35390361Sjulian * - NOTE: must stop MAC (tx) and requeue 40 MHz packets as 20 MHz when changing
35490361Sjulian *         from 20/40 => 20 only
35564921Smarcel */
35664921Smarcelvoid
35783221Smarcelar5416Set11nMac2040(struct ath_hal *ah, HAL_HT_MACMODE mode)
35883221Smarcel{
35983221Smarcel    uint32_t macmode;
36064921Smarcel
36164921Smarcel    /* Configure MAC for 20/40 operation */
36283221Smarcel    if (mode == HAL_HT_MACMODE_2040) {
36383221Smarcel        macmode = AR_2040_JOINED_RX_CLEAR;
36483221Smarcel    } else {
36583221Smarcel        macmode = 0;
366103216Sjulian    }
36790361Sjulian    OS_REG_WRITE(ah, AR_2040_MODE, macmode);
36883221Smarcel}
36983221Smarcel
37083221Smarcel/*
37183221Smarcel * Get Rx clear (control/extension channel)
37273856Sjhb *
37383221Smarcel * Returns active low (busy) for ctrl/ext channel
37464921Smarcel * Owl 2.0
37564921Smarcel */
37664921SmarcelHAL_HT_RXCLEAR
37783221Smarcelar5416Get11nRxClear(struct ath_hal *ah)
37883221Smarcel{
37983221Smarcel    HAL_HT_RXCLEAR rxclear = 0;
38083221Smarcel    uint32_t val;
38183221Smarcel
38283221Smarcel    val = OS_REG_READ(ah, AR_DIAG_SW);
38383221Smarcel
38464921Smarcel    /* control channel */
38564921Smarcel    if (val & AR_DIAG_RXCLEAR_CTL_LOW) {
38664921Smarcel        rxclear |= HAL_RX_CLEAR_CTL_LOW;
38764921Smarcel    }
38864921Smarcel    /* extension channel */
389104893Ssobomax    if (val & AR_DIAG_RXCLEAR_EXT_LOW) {
390104893Ssobomax        rxclear |= HAL_RX_CLEAR_EXT_LOW;
39164921Smarcel    }
392104893Ssobomax    return rxclear;
393104893Ssobomax}
394104893Ssobomax
395104893Ssobomax/*
396104893Ssobomax * Set Rx clear (control/extension channel)
397104893Ssobomax *
398104893Ssobomax * Useful for forcing the channel to appear busy for
399104893Ssobomax * debugging/diagnostics
400104893Ssobomax * Owl 2.0
401104893Ssobomax */
402104893Ssobomaxvoid
403104893Ssobomaxar5416Set11nRxClear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear)
404104893Ssobomax{
405104893Ssobomax    /* control channel */
406104893Ssobomax    if (rxclear & HAL_RX_CLEAR_CTL_LOW) {
407104893Ssobomax        OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_CTL_LOW);
408104893Ssobomax    } else {
409104893Ssobomax        OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_CTL_LOW);
410104893Ssobomax    }
411104893Ssobomax    /* extension channel */
412104893Ssobomax    if (rxclear & HAL_RX_CLEAR_EXT_LOW) {
413104893Ssobomax        OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_EXT_LOW);
41483366Sjulian    } else {
41564921Smarcel        OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_EXT_LOW);
41664921Smarcel    }
41783221Smarcel}
41864921Smarcel
419111797Sdes/* XXX shouldn't be here! */
42064921Smarcel#define	TU_TO_USEC(_tu)		((_tu) << 10)
42164921Smarcel
42264921SmarcelHAL_STATUS
42364921Smarcelar5416SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration,
42472543Sjlemon    uint32_t nextStart, HAL_QUIET_FLAG flag)
42572543Sjlemon{
426104984Sbde	uint32_t period_us = TU_TO_USEC(period); /* convert to us unit */
427104984Sbde	uint32_t nextStart_us = TU_TO_USEC(nextStart); /* convert to us unit */
42864921Smarcel	if (flag & HAL_QUIET_ENABLE) {
42964921Smarcel		if ((!nextStart) || (flag & HAL_QUIET_ADD_CURRENT_TSF)) {
430104893Ssobomax			/* Add the nextStart offset to the current TSF */
431104893Ssobomax			nextStart_us += OS_REG_READ(ah, AR_TSF_L32);
432104893Ssobomax		}
433104893Ssobomax		if (flag & HAL_QUIET_ADD_SWBA_RESP_TIME) {
434104893Ssobomax			nextStart_us += ah->ah_config.ah_sw_beacon_response_time;
435104893Ssobomax		}
436104893Ssobomax		OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
437104893Ssobomax		OS_REG_WRITE(ah, AR_QUIET2, SM(duration, AR_QUIET2_QUIET_DUR));
438104893Ssobomax		OS_REG_WRITE(ah, AR_QUIET_PERIOD, period_us);
439104893Ssobomax		OS_REG_WRITE(ah, AR_NEXT_QUIET, nextStart_us);
440104893Ssobomax		OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET);
441104893Ssobomax	} else {
442104893Ssobomax		OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET);
443104893Ssobomax	}
444104893Ssobomax	return HAL_OK;
445104893Ssobomax}
446104893Ssobomax#undef	TU_TO_USEC
44764921Smarcel
448104893SsobomaxHAL_STATUS
44964921Smarcelar5416GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
450104893Ssobomax        uint32_t capability, uint32_t *result)
45164921Smarcel{
452104893Ssobomax	switch (type) {
45364921Smarcel	case HAL_CAP_BB_HANG:
454104893Ssobomax		switch (capability) {
45564921Smarcel		case HAL_BB_HANG_RIFS:
45673213Sdillon			return (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ? HAL_OK : HAL_ENOTSUPP;
45773213Sdillon		case HAL_BB_HANG_DFS:
458104893Ssobomax			return (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ? HAL_OK : HAL_ENOTSUPP;
45964921Smarcel		case HAL_BB_HANG_RX_CLEAR:
46064921Smarcel			return AR_SREV_MERLIN(ah) ? HAL_OK : HAL_ENOTSUPP;
46164921Smarcel		}
46264921Smarcel		break;
46364921Smarcel	case HAL_CAP_MAC_HANG:
46464921Smarcel		return ((ah->ah_macVersion == AR_XSREV_VERSION_OWL_PCI) ||
46564921Smarcel		    (ah->ah_macVersion == AR_XSREV_VERSION_OWL_PCIE) ||
46664921Smarcel		    AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ?
46764921Smarcel			HAL_OK : HAL_ENOTSUPP;
46864921Smarcel	case HAL_CAP_DIVERSITY:		/* disable classic fast diversity */
46964921Smarcel		return HAL_ENXIO;
47064921Smarcel	case HAL_CAP_ENFORCE_TXOP:
47164921Smarcel		if (capability == 0)
47264921Smarcel			return (HAL_OK);
47364921Smarcel		if (capability != 1)
47464921Smarcel			return (HAL_ENOTSUPP);
47564921Smarcel		(*result) =
47664921Smarcel		    !! (AH5212(ah)->ah_miscMode & AR_PCU_TXOP_TBTT_LIMIT_ENA);
47764921Smarcel		return (HAL_OK);
47864921Smarcel	default:
47964921Smarcel		break;
48064921Smarcel	}
48164921Smarcel	return ar5212GetCapability(ah, type, capability, result);
48264921Smarcel}
483104893Ssobomax
48464921SmarcelHAL_BOOL
48567238Sgallatinar5416SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
48667238Sgallatin    u_int32_t capability, u_int32_t setting, HAL_STATUS *status)
48767238Sgallatin{
48867238Sgallatin	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
48967238Sgallatin
49067238Sgallatin	switch (type) {
49167238Sgallatin	case HAL_CAP_RX_CHAINMASK:
49267238Sgallatin		setting &= ath_hal_eepromGet(ah, AR_EEP_RXMASK, NULL);
49367238Sgallatin		pCap->halRxChainMask = setting;
49467238Sgallatin		if (owl_get_ntxchains(setting) > 2)
49567238Sgallatin			pCap->halRxStreams = 2;
49667238Sgallatin		else
49767238Sgallatin			pCap->halRxStreams = 1;
49867238Sgallatin		return AH_TRUE;
49967238Sgallatin	case HAL_CAP_TX_CHAINMASK:
50067238Sgallatin		setting &= ath_hal_eepromGet(ah, AR_EEP_TXMASK, NULL);
50171494Sjhb		pCap->halTxChainMask = setting;
50267238Sgallatin		if (owl_get_ntxchains(setting) > 2)
50367238Sgallatin			pCap->halTxStreams = 2;
50467238Sgallatin		else
50567238Sgallatin			pCap->halTxStreams = 1;
50664921Smarcel		return AH_TRUE;
507104893Ssobomax	case HAL_CAP_ENFORCE_TXOP:
508104893Ssobomax		if (capability != 1)
50964921Smarcel			return AH_FALSE;
51064921Smarcel		if (setting) {
51164921Smarcel			AH5212(ah)->ah_miscMode
51264921Smarcel			    |= AR_PCU_TXOP_TBTT_LIMIT_ENA;
51364921Smarcel			OS_REG_SET_BIT(ah, AR_MISC_MODE,
51464921Smarcel			    AR_PCU_TXOP_TBTT_LIMIT_ENA);
51564921Smarcel		} else {
51664921Smarcel			AH5212(ah)->ah_miscMode
51764921Smarcel			    &= ~AR_PCU_TXOP_TBTT_LIMIT_ENA;
51864921Smarcel			OS_REG_CLR_BIT(ah, AR_MISC_MODE,
51964921Smarcel			    AR_PCU_TXOP_TBTT_LIMIT_ENA);
520104893Ssobomax		}
521104893Ssobomax		return AH_TRUE;
52264921Smarcel	default:
52364921Smarcel		break;
524104893Ssobomax	}
525104893Ssobomax	return ar5212SetCapability(ah, type, capability, setting, status);
52664921Smarcel}
52764921Smarcel
528104893Ssobomaxstatic int ar5416DetectMacHang(struct ath_hal *ah);
529104893Ssobomaxstatic int ar5416DetectBBHang(struct ath_hal *ah);
53064921Smarcel
53164921SmarcelHAL_BOOL
53264921Smarcelar5416GetDiagState(struct ath_hal *ah, int request,
53372543Sjlemon	const void *args, uint32_t argsize,
53472543Sjlemon	void **result, uint32_t *resultsize)
53572543Sjlemon{
53672543Sjlemon	struct ath_hal_5416 *ahp = AH5416(ah);
53764921Smarcel	int hangs;
53864921Smarcel
53983366Sjulian	if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize))
54064921Smarcel		return AH_TRUE;
54164921Smarcel	switch (request) {
54264921Smarcel	case HAL_DIAG_EEPROM:
54383366Sjulian		return ath_hal_eepromDiag(ah, request,
54464921Smarcel		    args, argsize, result, resultsize);
54564921Smarcel	case HAL_DIAG_CHECK_HANGS:
54664921Smarcel		if (argsize != sizeof(int))
54764921Smarcel			return AH_FALSE;
54864921Smarcel		hangs = *(const int *) args;
54972543Sjlemon		ahp->ah_hangs = 0;
55072543Sjlemon		if (hangs & HAL_BB_HANGS)
55164921Smarcel			ahp->ah_hangs |= ar5416DetectBBHang(ah);
55264921Smarcel		/* NB: if BB is hung MAC will be hung too so skip check */
55383366Sjulian		if (ahp->ah_hangs == 0 && (hangs & HAL_MAC_HANGS))
55483366Sjulian			ahp->ah_hangs |= ar5416DetectMacHang(ah);
55564921Smarcel		*result = &ahp->ah_hangs;
55683366Sjulian		*resultsize = sizeof(ahp->ah_hangs);
55764921Smarcel		return AH_TRUE;
55864921Smarcel	}
55964921Smarcel	return ar5212GetDiagState(ah, request,
56083366Sjulian	    args, argsize, result, resultsize);
56164921Smarcel}
56283366Sjulian
56364921SmarcelHAL_BOOL
56464921Smarcelar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan,
56564921Smarcel    HAL_BOOL enable)
56683366Sjulian{
56783366Sjulian	uint32_t val;
56864921Smarcel	HAL_BOOL is_chan_2g = AH_FALSE;
56964921Smarcel	HAL_BOOL is_ht40 = AH_FALSE;
57064921Smarcel
57164921Smarcel	if (chan)
57283366Sjulian		is_chan_2g = IEEE80211_IS_CHAN_2GHZ(chan);
57364921Smarcel
57464921Smarcel	if (chan)
57564921Smarcel		is_ht40 = IEEE80211_IS_CHAN_HT40(chan);
57664921Smarcel
57764921Smarcel	/* Only support disabling RIFS delay for now */
57864921Smarcel	HALASSERT(enable == AH_FALSE);
57964921Smarcel
58064921Smarcel	if (enable == AH_TRUE)
58164921Smarcel		return AH_FALSE;
58264921Smarcel
58364921Smarcel	/* Change RIFS init delay to 0 */
58464921Smarcel	val = OS_REG_READ(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS);
58583366Sjulian	val &= ~AR_PHY_RIFS_INIT_DELAY;
58664921Smarcel	OS_REG_WRITE(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS, val);
58764921Smarcel
58864921Smarcel	/*
58983366Sjulian	 * For Owl, RIFS RX parameters are controlled differently;
59064921Smarcel	 * it isn't enabled in the inivals by default.
59164921Smarcel	 *
59264921Smarcel	 * For Sowl/Howl, RIFS RX is enabled in the inivals by default;
59364921Smarcel	 * the following code sets them back to non-RIFS values.
59464921Smarcel	 *
59593593Sjhb	 * For > Sowl/Howl, RIFS RX can be left on by default and so
59664921Smarcel	 * this function shouldn't be called.
59791406Sjhb	 */
59883981Srwatson	if ((! AR_SREV_SOWL(ah)) && (! AR_SREV_HOWL(ah)))
59983366Sjulian		return AH_TRUE;
60064921Smarcel
60164921Smarcel	/* Reset search delay to default values */
60264921Smarcel	if (is_chan_2g)
60364921Smarcel		if (is_ht40)
60464921Smarcel			OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x268);
605105441Smarkm		else
60664921Smarcel			OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x134);
60764921Smarcel	else
60864921Smarcel		if (is_ht40)
60964921Smarcel			OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x370);
61064921Smarcel		else
61183221Smarcel			OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x1b8);
61264921Smarcel
61364921Smarcel	return AH_TRUE;
61464921Smarcel}
61564921Smarcel
61664921Smarcelstatic HAL_BOOL
61764921Smarcelar5416CompareDbgHang(struct ath_hal *ah, const mac_dbg_regs_t *regs,
61864921Smarcel    const hal_mac_hang_check_t *check)
61964921Smarcel{
62064921Smarcel	int found_states;
62164921Smarcel
62264921Smarcel	found_states = 0;
62364921Smarcel	if (check->states & dcu_chain_state) {
62464921Smarcel		int i;
62564921Smarcel
62664921Smarcel		for (i = 0; i < 6; i++) {
62783366Sjulian			if (((regs->dma_dbg_4 >> (5*i)) & 0x1f) ==
62883366Sjulian			    check->dcu_chain_state)
62964921Smarcel				found_states |= dcu_chain_state;
63064921Smarcel		}
63164921Smarcel		for (i = 0; i < 4; i++) {
63264921Smarcel			if (((regs->dma_dbg_5 >> (5*i)) & 0x1f) ==
63364921Smarcel			    check->dcu_chain_state)
63464921Smarcel				found_states |= dcu_chain_state;
63564921Smarcel		}
63664921Smarcel	}
63764921Smarcel	if (check->states & dcu_complete_state) {
63864921Smarcel		if ((regs->dma_dbg_6 & 0x3) == check->dcu_complete_state)
63964921Smarcel			found_states |= dcu_complete_state;
64064921Smarcel	}
64164921Smarcel	if (check->states & qcu_stitch_state) {
64264921Smarcel		if (((regs->dma_dbg_3 >> 18) & 0xf) == check->qcu_stitch_state)
64364921Smarcel			found_states |= qcu_stitch_state;
64464921Smarcel	}
64564921Smarcel	if (check->states & qcu_fetch_state) {
64664921Smarcel		if (((regs->dma_dbg_3 >> 22) & 0xf) == check->qcu_fetch_state)
64764921Smarcel			found_states |= qcu_fetch_state;
64864921Smarcel	}
64964921Smarcel	if (check->states & qcu_complete_state) {
65064921Smarcel		if (((regs->dma_dbg_3 >> 26) & 0x7) == check->qcu_complete_state)
65164921Smarcel			found_states |= qcu_complete_state;
65264921Smarcel	}
65364921Smarcel	return (found_states == check->states);
65464921Smarcel}
65564921Smarcel
65664921Smarcel#define NUM_STATUS_READS 50
65783366Sjulian
65864921Smarcelstatic int
65964921Smarcelar5416DetectMacHang(struct ath_hal *ah)
66064921Smarcel{
66164921Smarcel	static const hal_mac_hang_check_t hang_sig1 = {
66264921Smarcel		.dcu_chain_state	= 0x6,
66364921Smarcel		.dcu_complete_state	= 0x1,
66464921Smarcel		.states			= dcu_chain_state
66564921Smarcel					| dcu_complete_state,
66664921Smarcel	};
66764921Smarcel	static const hal_mac_hang_check_t hang_sig2 = {
66864921Smarcel		.qcu_stitch_state	= 0x9,
66964921Smarcel		.qcu_fetch_state	= 0x8,
67064921Smarcel		.qcu_complete_state	= 0x4,
67164921Smarcel		.states			= qcu_stitch_state
67264921Smarcel					| qcu_fetch_state
67383366Sjulian					| qcu_complete_state,
67464921Smarcel        };
67583221Smarcel	mac_dbg_regs_t mac_dbg;
67683221Smarcel	int i;
67764921Smarcel
67864921Smarcel	mac_dbg.dma_dbg_3 = OS_REG_READ(ah, AR_DMADBG_3);
67964921Smarcel	mac_dbg.dma_dbg_4 = OS_REG_READ(ah, AR_DMADBG_4);
68072543Sjlemon	mac_dbg.dma_dbg_5 = OS_REG_READ(ah, AR_DMADBG_5);
68172543Sjlemon	mac_dbg.dma_dbg_6 = OS_REG_READ(ah, AR_DMADBG_6);
68272543Sjlemon	for (i = 1; i <= NUM_STATUS_READS; i++) {
68364921Smarcel		if (mac_dbg.dma_dbg_3 != OS_REG_READ(ah, AR_DMADBG_3) ||
68464921Smarcel		    mac_dbg.dma_dbg_4 != OS_REG_READ(ah, AR_DMADBG_4) ||
68564921Smarcel		    mac_dbg.dma_dbg_5 != OS_REG_READ(ah, AR_DMADBG_5) ||
686111797Sdes		    mac_dbg.dma_dbg_6 != OS_REG_READ(ah, AR_DMADBG_6))
68764921Smarcel			return 0;
68864921Smarcel	}
68964921Smarcel
69064921Smarcel	if (ar5416CompareDbgHang(ah, &mac_dbg, &hang_sig1))
69164921Smarcel		return HAL_MAC_HANG_SIG1;
69264921Smarcel	if (ar5416CompareDbgHang(ah, &mac_dbg, &hang_sig2))
69364921Smarcel		return HAL_MAC_HANG_SIG2;
69464921Smarcel
69564921Smarcel	HALDEBUG(ah, HAL_DEBUG_HANG, "%s Found an unknown MAC hang signature "
69683366Sjulian	    "DMADBG_3=0x%x DMADBG_4=0x%x DMADBG_5=0x%x DMADBG_6=0x%x\n",
69764921Smarcel	    __func__, mac_dbg.dma_dbg_3, mac_dbg.dma_dbg_4, mac_dbg.dma_dbg_5,
69864921Smarcel	    mac_dbg.dma_dbg_6);
69964921Smarcel
70064921Smarcel	return 0;
70164921Smarcel}
70264921Smarcel
70364921Smarcel/*
704111797Sdes * Determine if the baseband using the Observation Bus Register
70564921Smarcel */
70664921Smarcelstatic int
70764921Smarcelar5416DetectBBHang(struct ath_hal *ah)
70864921Smarcel{
70964921Smarcel#define N(a) (sizeof(a)/sizeof(a[0]))
71064921Smarcel	/*
71164921Smarcel	 * Check the PCU Observation Bus 1 register (0x806c)
71264921Smarcel	 * NUM_STATUS_READS times
71364921Smarcel	 *
71464921Smarcel	 * 4 known BB hang signatures -
71564921Smarcel	 * [1] bits 8,9,11 are 0. State machine state (bits 25-31) is 0x1E
71683366Sjulian	 * [2] bits 8,9 are 1, bit 11 is 0. State machine state
71764921Smarcel	 *     (bits 25-31) is 0x52
718102814Siedowse	 * [3] bits 8,9 are 1, bit 11 is 0. State machine state
71983221Smarcel	 *     (bits 25-31) is 0x18
72064921Smarcel	 * [4] bit 10 is 1, bit 11 is 0. WEP state (bits 12-17) is 0x2,
72164921Smarcel	 *     Rx State (bits 20-24) is 0x7.
72272543Sjlemon	 */
72372543Sjlemon	static const struct {
72464921Smarcel		uint32_t val;
72564921Smarcel		uint32_t mask;
72664921Smarcel		int code;
72764921Smarcel	} hang_list[] = {
728102814Siedowse		/* Reg Value   Reg Mask    Hang Code XXX */
729102814Siedowse		{ 0x1E000000, 0x7E000B00, HAL_BB_HANG_DFS },
73064921Smarcel		{ 0x52000B00, 0x7E000B00, HAL_BB_HANG_RIFS },
73164921Smarcel		{ 0x18000B00, 0x7E000B00, HAL_BB_HANG_RX_CLEAR },
73264921Smarcel		{ 0x00702400, 0x7E7FFFEF, HAL_BB_HANG_RX_CLEAR }
733105441Smarkm	};
73464921Smarcel	uint32_t hang_sig;
73583221Smarcel	int i;
736102814Siedowse
73764921Smarcel	hang_sig = OS_REG_READ(ah, AR_OBSERV_1);
73864921Smarcel	for (i = 1; i <= NUM_STATUS_READS; i++) {
73964921Smarcel		if (hang_sig != OS_REG_READ(ah, AR_OBSERV_1))
74072543Sjlemon			return 0;
74172543Sjlemon	}
74272543Sjlemon	for (i = 0; i < N(hang_list); i++)
74364921Smarcel		if ((hang_sig & hang_list[i].mask) == hang_list[i].val) {
74464921Smarcel			HALDEBUG(ah, HAL_DEBUG_HANG,
74583221Smarcel			    "%s BB hang, signature 0x%x, code 0x%x\n",
74664921Smarcel			    __func__, hang_sig, hang_list[i].code);
74764921Smarcel			return hang_list[i].code;
74883221Smarcel		}
74964921Smarcel
75064921Smarcel	HALDEBUG(ah, HAL_DEBUG_HANG, "%s Found an unknown BB hang signature! "
75164921Smarcel	    "<0x806c>=0x%x\n", __func__, hang_sig);
752102814Siedowse
753102814Siedowse	return 0;
75464921Smarcel#undef N
75564921Smarcel}
75664921Smarcel#undef NUM_STATUS_READS
75783366Sjulian