1*6e778a7eSPedro F. Giffuni /*-
2*6e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
3*6e778a7eSPedro F. Giffuni *
459efa8b5SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
514779705SSam Leffler * Copyright (c) 2002-2008 Atheros Communications, Inc.
614779705SSam Leffler *
714779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler *
1114779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler
2114779705SSam Leffler #include "ah.h"
2214779705SSam Leffler #include "ah_internal.h"
2393cad1bdSAdrian Chadd #include "ah_desc.h"
2414779705SSam Leffler
2514779705SSam Leffler #include "ar5212/ar5212.h"
2614779705SSam Leffler #include "ar5212/ar5212reg.h"
2714779705SSam Leffler #include "ar5212/ar5212desc.h"
2814779705SSam Leffler #include "ar5212/ar5212phy.h"
2914779705SSam Leffler #ifdef AH_SUPPORT_5311
3014779705SSam Leffler #include "ar5212/ar5311reg.h"
3114779705SSam Leffler #endif
3214779705SSam Leffler
3314779705SSam Leffler #ifdef AH_NEED_DESC_SWAP
3414779705SSam Leffler static void ar5212SwapTxDesc(struct ath_desc *ds);
3514779705SSam Leffler #endif
3614779705SSam Leffler
3714779705SSam Leffler /*
3814779705SSam Leffler * Update Tx FIFO trigger level.
3914779705SSam Leffler *
4014779705SSam Leffler * Set bIncTrigLevel to TRUE to increase the trigger level.
4114779705SSam Leffler * Set bIncTrigLevel to FALSE to decrease the trigger level.
4214779705SSam Leffler *
4314779705SSam Leffler * Returns TRUE if the trigger level was updated
4414779705SSam Leffler */
4514779705SSam Leffler HAL_BOOL
ar5212UpdateTxTrigLevel(struct ath_hal * ah,HAL_BOOL bIncTrigLevel)4614779705SSam Leffler ar5212UpdateTxTrigLevel(struct ath_hal *ah, HAL_BOOL bIncTrigLevel)
4714779705SSam Leffler {
4814779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
4914779705SSam Leffler uint32_t txcfg, curLevel, newLevel;
5014779705SSam Leffler HAL_INT omask;
5114779705SSam Leffler
52256796dbSRui Paulo if (ahp->ah_txTrigLev >= ahp->ah_maxTxTrigLev)
539425e26bSRui Paulo return AH_FALSE;
549425e26bSRui Paulo
5514779705SSam Leffler /*
5614779705SSam Leffler * Disable interrupts while futzing with the fifo level.
5714779705SSam Leffler */
58d4aef82bSRui Paulo omask = ath_hal_setInterrupts(ah, ahp->ah_maskReg &~ HAL_INT_GLOBAL);
5914779705SSam Leffler
6014779705SSam Leffler txcfg = OS_REG_READ(ah, AR_TXCFG);
6114779705SSam Leffler curLevel = MS(txcfg, AR_FTRIG);
6214779705SSam Leffler newLevel = curLevel;
6314779705SSam Leffler if (bIncTrigLevel) { /* increase the trigger level */
64256796dbSRui Paulo if (curLevel < ahp->ah_maxTxTrigLev)
6514779705SSam Leffler newLevel++;
6614779705SSam Leffler } else if (curLevel > MIN_TX_FIFO_THRESHOLD)
6714779705SSam Leffler newLevel--;
6814779705SSam Leffler if (newLevel != curLevel)
6914779705SSam Leffler /* Update the trigger level */
7014779705SSam Leffler OS_REG_WRITE(ah, AR_TXCFG,
7114779705SSam Leffler (txcfg &~ AR_FTRIG) | SM(newLevel, AR_FTRIG));
7214779705SSam Leffler
73256796dbSRui Paulo ahp->ah_txTrigLev = newLevel;
749425e26bSRui Paulo
7514779705SSam Leffler /* re-enable chip interrupts */
76d4aef82bSRui Paulo ath_hal_setInterrupts(ah, omask);
7714779705SSam Leffler
7814779705SSam Leffler return (newLevel != curLevel);
7914779705SSam Leffler }
8014779705SSam Leffler
8114779705SSam Leffler /*
8214779705SSam Leffler * Set the properties of the tx queue with the parameters
8314779705SSam Leffler * from qInfo.
8414779705SSam Leffler */
8514779705SSam Leffler HAL_BOOL
ar5212SetTxQueueProps(struct ath_hal * ah,int q,const HAL_TXQ_INFO * qInfo)8614779705SSam Leffler ar5212SetTxQueueProps(struct ath_hal *ah, int q, const HAL_TXQ_INFO *qInfo)
8714779705SSam Leffler {
8814779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
8914779705SSam Leffler HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
9014779705SSam Leffler
9114779705SSam Leffler if (q >= pCap->halTotalQueues) {
9214779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
9314779705SSam Leffler __func__, q);
9414779705SSam Leffler return AH_FALSE;
9514779705SSam Leffler }
9614779705SSam Leffler return ath_hal_setTxQProps(ah, &ahp->ah_txq[q], qInfo);
9714779705SSam Leffler }
9814779705SSam Leffler
9914779705SSam Leffler /*
10014779705SSam Leffler * Return the properties for the specified tx queue.
10114779705SSam Leffler */
10214779705SSam Leffler HAL_BOOL
ar5212GetTxQueueProps(struct ath_hal * ah,int q,HAL_TXQ_INFO * qInfo)10314779705SSam Leffler ar5212GetTxQueueProps(struct ath_hal *ah, int q, HAL_TXQ_INFO *qInfo)
10414779705SSam Leffler {
10514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
10614779705SSam Leffler HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
10714779705SSam Leffler
10814779705SSam Leffler if (q >= pCap->halTotalQueues) {
10914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
11014779705SSam Leffler __func__, q);
11114779705SSam Leffler return AH_FALSE;
11214779705SSam Leffler }
11314779705SSam Leffler return ath_hal_getTxQProps(ah, qInfo, &ahp->ah_txq[q]);
11414779705SSam Leffler }
11514779705SSam Leffler
11614779705SSam Leffler /*
11714779705SSam Leffler * Allocate and initialize a tx DCU/QCU combination.
11814779705SSam Leffler */
11914779705SSam Leffler int
ar5212SetupTxQueue(struct ath_hal * ah,HAL_TX_QUEUE type,const HAL_TXQ_INFO * qInfo)12014779705SSam Leffler ar5212SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type,
12114779705SSam Leffler const HAL_TXQ_INFO *qInfo)
12214779705SSam Leffler {
12314779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
12414779705SSam Leffler HAL_TX_QUEUE_INFO *qi;
12514779705SSam Leffler HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
12614779705SSam Leffler int q, defqflags;
12714779705SSam Leffler
12814779705SSam Leffler /* by default enable OK+ERR+DESC+URN interrupts */
12914779705SSam Leffler defqflags = HAL_TXQ_TXOKINT_ENABLE
13014779705SSam Leffler | HAL_TXQ_TXERRINT_ENABLE
13114779705SSam Leffler | HAL_TXQ_TXDESCINT_ENABLE
13214779705SSam Leffler | HAL_TXQ_TXURNINT_ENABLE;
13314779705SSam Leffler /* XXX move queue assignment to driver */
13414779705SSam Leffler switch (type) {
13514779705SSam Leffler case HAL_TX_QUEUE_BEACON:
13614779705SSam Leffler q = pCap->halTotalQueues-1; /* highest priority */
13714779705SSam Leffler defqflags |= HAL_TXQ_DBA_GATED
13814779705SSam Leffler | HAL_TXQ_CBR_DIS_QEMPTY
13914779705SSam Leffler | HAL_TXQ_ARB_LOCKOUT_GLOBAL
14014779705SSam Leffler | HAL_TXQ_BACKOFF_DISABLE;
14114779705SSam Leffler break;
14214779705SSam Leffler case HAL_TX_QUEUE_CAB:
14314779705SSam Leffler q = pCap->halTotalQueues-2; /* next highest priority */
14414779705SSam Leffler defqflags |= HAL_TXQ_DBA_GATED
14514779705SSam Leffler | HAL_TXQ_CBR_DIS_QEMPTY
14614779705SSam Leffler | HAL_TXQ_CBR_DIS_BEMPTY
14714779705SSam Leffler | HAL_TXQ_ARB_LOCKOUT_GLOBAL
14814779705SSam Leffler | HAL_TXQ_BACKOFF_DISABLE;
14914779705SSam Leffler break;
15014779705SSam Leffler case HAL_TX_QUEUE_UAPSD:
15114779705SSam Leffler q = pCap->halTotalQueues-3; /* nextest highest priority */
15214779705SSam Leffler if (ahp->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE) {
15314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
15414779705SSam Leffler "%s: no available UAPSD tx queue\n", __func__);
15514779705SSam Leffler return -1;
15614779705SSam Leffler }
15714779705SSam Leffler break;
15814779705SSam Leffler case HAL_TX_QUEUE_DATA:
15914779705SSam Leffler for (q = 0; q < pCap->halTotalQueues; q++)
16014779705SSam Leffler if (ahp->ah_txq[q].tqi_type == HAL_TX_QUEUE_INACTIVE)
16114779705SSam Leffler break;
16214779705SSam Leffler if (q == pCap->halTotalQueues) {
16314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
16414779705SSam Leffler "%s: no available tx queue\n", __func__);
16514779705SSam Leffler return -1;
16614779705SSam Leffler }
16714779705SSam Leffler break;
16814779705SSam Leffler default:
16914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
17014779705SSam Leffler "%s: bad tx queue type %u\n", __func__, type);
17114779705SSam Leffler return -1;
17214779705SSam Leffler }
17314779705SSam Leffler
17414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q);
17514779705SSam Leffler
17614779705SSam Leffler qi = &ahp->ah_txq[q];
17714779705SSam Leffler if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) {
17814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: tx queue %u already active\n",
17914779705SSam Leffler __func__, q);
18014779705SSam Leffler return -1;
18114779705SSam Leffler }
18214779705SSam Leffler OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO));
18314779705SSam Leffler qi->tqi_type = type;
18414779705SSam Leffler if (qInfo == AH_NULL) {
18514779705SSam Leffler qi->tqi_qflags = defqflags;
18614779705SSam Leffler qi->tqi_aifs = INIT_AIFS;
18714779705SSam Leffler qi->tqi_cwmin = HAL_TXQ_USEDEFAULT; /* NB: do at reset */
18814779705SSam Leffler qi->tqi_cwmax = INIT_CWMAX;
18914779705SSam Leffler qi->tqi_shretry = INIT_SH_RETRY;
19014779705SSam Leffler qi->tqi_lgretry = INIT_LG_RETRY;
19114779705SSam Leffler qi->tqi_physCompBuf = 0;
19214779705SSam Leffler } else {
19314779705SSam Leffler qi->tqi_physCompBuf = qInfo->tqi_compBuf;
19414779705SSam Leffler (void) ar5212SetTxQueueProps(ah, q, qInfo);
19514779705SSam Leffler }
19614779705SSam Leffler /* NB: must be followed by ar5212ResetTxQueue */
19714779705SSam Leffler return q;
19814779705SSam Leffler }
19914779705SSam Leffler
20014779705SSam Leffler /*
20114779705SSam Leffler * Update the h/w interrupt registers to reflect a tx q's configuration.
20214779705SSam Leffler */
20314779705SSam Leffler static void
setTxQInterrupts(struct ath_hal * ah,HAL_TX_QUEUE_INFO * qi)20414779705SSam Leffler setTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi)
20514779705SSam Leffler {
20614779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
20714779705SSam Leffler
20814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
20914779705SSam Leffler "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", __func__,
21014779705SSam Leffler ahp->ah_txOkInterruptMask, ahp->ah_txErrInterruptMask,
21114779705SSam Leffler ahp->ah_txDescInterruptMask, ahp->ah_txEolInterruptMask,
21214779705SSam Leffler ahp->ah_txUrnInterruptMask);
21314779705SSam Leffler
21414779705SSam Leffler OS_REG_WRITE(ah, AR_IMR_S0,
21514779705SSam Leffler SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK)
21614779705SSam Leffler | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC)
21714779705SSam Leffler );
21814779705SSam Leffler OS_REG_WRITE(ah, AR_IMR_S1,
21914779705SSam Leffler SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR)
22014779705SSam Leffler | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL)
22114779705SSam Leffler );
22214779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_IMR_S2,
22314779705SSam Leffler AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask);
22414779705SSam Leffler }
22514779705SSam Leffler
22614779705SSam Leffler /*
22714779705SSam Leffler * Free a tx DCU/QCU combination.
22814779705SSam Leffler */
22914779705SSam Leffler HAL_BOOL
ar5212ReleaseTxQueue(struct ath_hal * ah,u_int q)23014779705SSam Leffler ar5212ReleaseTxQueue(struct ath_hal *ah, u_int q)
23114779705SSam Leffler {
23214779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
23314779705SSam Leffler HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
23414779705SSam Leffler HAL_TX_QUEUE_INFO *qi;
23514779705SSam Leffler
23614779705SSam Leffler if (q >= pCap->halTotalQueues) {
23714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
23814779705SSam Leffler __func__, q);
23914779705SSam Leffler return AH_FALSE;
24014779705SSam Leffler }
24114779705SSam Leffler qi = &ahp->ah_txq[q];
24214779705SSam Leffler if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
24314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n",
24414779705SSam Leffler __func__, q);
24514779705SSam Leffler return AH_FALSE;
24614779705SSam Leffler }
24714779705SSam Leffler
24814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: release queue %u\n", __func__, q);
24914779705SSam Leffler
25014779705SSam Leffler qi->tqi_type = HAL_TX_QUEUE_INACTIVE;
25114779705SSam Leffler ahp->ah_txOkInterruptMask &= ~(1 << q);
25214779705SSam Leffler ahp->ah_txErrInterruptMask &= ~(1 << q);
25314779705SSam Leffler ahp->ah_txDescInterruptMask &= ~(1 << q);
25414779705SSam Leffler ahp->ah_txEolInterruptMask &= ~(1 << q);
25514779705SSam Leffler ahp->ah_txUrnInterruptMask &= ~(1 << q);
25614779705SSam Leffler setTxQInterrupts(ah, qi);
25714779705SSam Leffler
25814779705SSam Leffler return AH_TRUE;
25914779705SSam Leffler }
26014779705SSam Leffler
26114779705SSam Leffler /*
26214779705SSam Leffler * Set the retry, aifs, cwmin/max, readyTime regs for specified queue
26314779705SSam Leffler * Assumes:
26414779705SSam Leffler * phwChannel has been set to point to the current channel
26514779705SSam Leffler */
266353d2977SAdrian Chadd #define TU_TO_USEC(_tu) ((_tu) << 10)
26714779705SSam Leffler HAL_BOOL
ar5212ResetTxQueue(struct ath_hal * ah,u_int q)26814779705SSam Leffler ar5212ResetTxQueue(struct ath_hal *ah, u_int q)
26914779705SSam Leffler {
27014779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
27114779705SSam Leffler HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
27259efa8b5SSam Leffler const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
27314779705SSam Leffler HAL_TX_QUEUE_INFO *qi;
274353d2977SAdrian Chadd uint32_t cwMin, chanCwMin, qmisc, dmisc;
27514779705SSam Leffler
27614779705SSam Leffler if (q >= pCap->halTotalQueues) {
27714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
27814779705SSam Leffler __func__, q);
27914779705SSam Leffler return AH_FALSE;
28014779705SSam Leffler }
28114779705SSam Leffler qi = &ahp->ah_txq[q];
28214779705SSam Leffler if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
28314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n",
28414779705SSam Leffler __func__, q);
28514779705SSam Leffler return AH_TRUE; /* XXX??? */
28614779705SSam Leffler }
28714779705SSam Leffler
28814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: reset queue %u\n", __func__, q);
28914779705SSam Leffler
29014779705SSam Leffler if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) {
29114779705SSam Leffler /*
29214779705SSam Leffler * Select cwmin according to channel type.
29314779705SSam Leffler * NB: chan can be NULL during attach
29414779705SSam Leffler */
29559efa8b5SSam Leffler if (chan && IEEE80211_IS_CHAN_B(chan))
29614779705SSam Leffler chanCwMin = INIT_CWMIN_11B;
29714779705SSam Leffler else
29814779705SSam Leffler chanCwMin = INIT_CWMIN;
29914779705SSam Leffler /* make sure that the CWmin is of the form (2^n - 1) */
30014779705SSam Leffler for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1)
30114779705SSam Leffler ;
30214779705SSam Leffler } else
30314779705SSam Leffler cwMin = qi->tqi_cwmin;
30414779705SSam Leffler
30514779705SSam Leffler /* set cwMin/Max and AIFS values */
30614779705SSam Leffler OS_REG_WRITE(ah, AR_DLCL_IFS(q),
30714779705SSam Leffler SM(cwMin, AR_D_LCL_IFS_CWMIN)
30814779705SSam Leffler | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX)
30914779705SSam Leffler | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS));
31014779705SSam Leffler
31114779705SSam Leffler /* Set retry limit values */
31214779705SSam Leffler OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q),
31314779705SSam Leffler SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH)
31414779705SSam Leffler | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG)
31514779705SSam Leffler | SM(qi->tqi_lgretry, AR_D_RETRY_LIMIT_FR_LG)
31614779705SSam Leffler | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH)
31714779705SSam Leffler );
31814779705SSam Leffler
31914779705SSam Leffler /* NB: always enable early termination on the QCU */
32014779705SSam Leffler qmisc = AR_Q_MISC_DCU_EARLY_TERM_REQ
32114779705SSam Leffler | SM(AR_Q_MISC_FSP_ASAP, AR_Q_MISC_FSP);
32214779705SSam Leffler
32314779705SSam Leffler /* NB: always enable DCU to wait for next fragment from QCU */
32414779705SSam Leffler dmisc = AR_D_MISC_FRAG_WAIT_EN;
32514779705SSam Leffler
32614779705SSam Leffler #ifdef AH_SUPPORT_5311
32714779705SSam Leffler if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) {
32814779705SSam Leffler /* Configure DCU to use the global sequence count */
32914779705SSam Leffler dmisc |= AR5311_D_MISC_SEQ_NUM_CONTROL;
33014779705SSam Leffler }
33114779705SSam Leffler #endif
33214779705SSam Leffler /* multiqueue support */
33314779705SSam Leffler if (qi->tqi_cbrPeriod) {
33414779705SSam Leffler OS_REG_WRITE(ah, AR_QCBRCFG(q),
33514779705SSam Leffler SM(qi->tqi_cbrPeriod,AR_Q_CBRCFG_CBR_INTERVAL)
33614779705SSam Leffler | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_CBR_OVF_THRESH));
33714779705SSam Leffler qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_CBR;
33814779705SSam Leffler if (qi->tqi_cbrOverflowLimit)
33914779705SSam Leffler qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT;
34014779705SSam Leffler }
34114779705SSam Leffler if (qi->tqi_readyTime) {
34214779705SSam Leffler OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
34314779705SSam Leffler SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT)
34414779705SSam Leffler | AR_Q_RDYTIMECFG_ENA);
34514779705SSam Leffler }
34614779705SSam Leffler
34714779705SSam Leffler OS_REG_WRITE(ah, AR_DCHNTIME(q),
34814779705SSam Leffler SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR)
34914779705SSam Leffler | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0));
35014779705SSam Leffler
35114779705SSam Leffler if (qi->tqi_readyTime &&
35214779705SSam Leffler (qi->tqi_qflags & HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE))
35314779705SSam Leffler qmisc |= AR_Q_MISC_RDYTIME_EXP_POLICY;
35414779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_DBA_GATED)
35514779705SSam Leffler qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_DBA_GATED;
35614779705SSam Leffler if (MS(qmisc, AR_Q_MISC_FSP) != AR_Q_MISC_FSP_ASAP) {
35714779705SSam Leffler /*
35814779705SSam Leffler * These are meangingful only when not scheduled asap.
35914779705SSam Leffler */
36014779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_BEMPTY)
36114779705SSam Leffler qmisc |= AR_Q_MISC_CBR_INCR_DIS0;
36214779705SSam Leffler else
36314779705SSam Leffler qmisc &= ~AR_Q_MISC_CBR_INCR_DIS0;
36414779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_QEMPTY)
36514779705SSam Leffler qmisc |= AR_Q_MISC_CBR_INCR_DIS1;
36614779705SSam Leffler else
36714779705SSam Leffler qmisc &= ~AR_Q_MISC_CBR_INCR_DIS1;
36814779705SSam Leffler }
36914779705SSam Leffler
37014779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_BACKOFF_DISABLE)
37114779705SSam Leffler dmisc |= AR_D_MISC_POST_FR_BKOFF_DIS;
37214779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE)
37314779705SSam Leffler dmisc |= AR_D_MISC_FRAG_BKOFF_EN;
37414779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_GLOBAL)
37514779705SSam Leffler dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
37614779705SSam Leffler AR_D_MISC_ARB_LOCKOUT_CNTRL);
37714779705SSam Leffler else if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_INTRA)
37814779705SSam Leffler dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR,
37914779705SSam Leffler AR_D_MISC_ARB_LOCKOUT_CNTRL);
38014779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_IGNORE_VIRTCOL)
38114779705SSam Leffler dmisc |= SM(AR_D_MISC_VIR_COL_HANDLING_IGNORE,
38214779705SSam Leffler AR_D_MISC_VIR_COL_HANDLING);
38314779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_SEQNUM_INC_DIS)
38414779705SSam Leffler dmisc |= AR_D_MISC_SEQ_NUM_INCR_DIS;
38514779705SSam Leffler
38614779705SSam Leffler /*
38714779705SSam Leffler * Fillin type-dependent bits. Most of this can be
38814779705SSam Leffler * removed by specifying the queue parameters in the
38914779705SSam Leffler * driver; it's here for backwards compatibility.
39014779705SSam Leffler */
39114779705SSam Leffler switch (qi->tqi_type) {
39214779705SSam Leffler case HAL_TX_QUEUE_BEACON: /* beacon frames */
39314779705SSam Leffler qmisc |= AR_Q_MISC_FSP_DBA_GATED
39414779705SSam Leffler | AR_Q_MISC_BEACON_USE
39514779705SSam Leffler | AR_Q_MISC_CBR_INCR_DIS1;
39614779705SSam Leffler
39714779705SSam Leffler dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
39814779705SSam Leffler AR_D_MISC_ARB_LOCKOUT_CNTRL)
39914779705SSam Leffler | AR_D_MISC_BEACON_USE
40014779705SSam Leffler | AR_D_MISC_POST_FR_BKOFF_DIS;
40114779705SSam Leffler break;
40214779705SSam Leffler case HAL_TX_QUEUE_CAB: /* CAB frames */
40314779705SSam Leffler /*
40414779705SSam Leffler * No longer Enable AR_Q_MISC_RDYTIME_EXP_POLICY,
40514779705SSam Leffler * There is an issue with the CAB Queue
40614779705SSam Leffler * not properly refreshing the Tx descriptor if
40714779705SSam Leffler * the TXE clear setting is used.
40814779705SSam Leffler */
40914779705SSam Leffler qmisc |= AR_Q_MISC_FSP_DBA_GATED
41014779705SSam Leffler | AR_Q_MISC_CBR_INCR_DIS1
41114779705SSam Leffler | AR_Q_MISC_CBR_INCR_DIS0;
41214779705SSam Leffler
413353d2977SAdrian Chadd if (qi->tqi_readyTime) {
414353d2977SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
415353d2977SAdrian Chadd "%s: using tqi_readyTime\n", __func__);
416353d2977SAdrian Chadd OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
417353d2977SAdrian Chadd SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) |
418353d2977SAdrian Chadd AR_Q_RDYTIMECFG_ENA);
419353d2977SAdrian Chadd } else {
420353d2977SAdrian Chadd int value;
42114779705SSam Leffler /*
42214779705SSam Leffler * NB: don't set default ready time if driver
42314779705SSam Leffler * has explicitly specified something. This is
42414779705SSam Leffler * here solely for backwards compatibility.
42514779705SSam Leffler */
426353d2977SAdrian Chadd /*
427353d2977SAdrian Chadd * XXX for now, hard-code a CAB interval of 70%
428353d2977SAdrian Chadd * XXX of the total beacon interval.
429353d2977SAdrian Chadd */
430353d2977SAdrian Chadd
431353d2977SAdrian Chadd value = (ahp->ah_beaconInterval * 70 / 100)
43237931a35SAdrian Chadd - (ah->ah_config.ah_sw_beacon_response_time -
433353d2977SAdrian Chadd + ah->ah_config.ah_dma_beacon_response_time)
434353d2977SAdrian Chadd - ah->ah_config.ah_additional_swba_backoff;
435353d2977SAdrian Chadd /*
436353d2977SAdrian Chadd * XXX Ensure it isn't too low - nothing lower
437353d2977SAdrian Chadd * XXX than 10 TU
438353d2977SAdrian Chadd */
439353d2977SAdrian Chadd if (value < 10)
440353d2977SAdrian Chadd value = 10;
441353d2977SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
442353d2977SAdrian Chadd "%s: defaulting to rdytime = %d uS\n",
443353d2977SAdrian Chadd __func__, value);
444353d2977SAdrian Chadd OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
445353d2977SAdrian Chadd SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) |
446353d2977SAdrian Chadd AR_Q_RDYTIMECFG_ENA);
44714779705SSam Leffler }
44814779705SSam Leffler dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
44914779705SSam Leffler AR_D_MISC_ARB_LOCKOUT_CNTRL);
45014779705SSam Leffler break;
45114779705SSam Leffler default: /* NB: silence compiler */
45214779705SSam Leffler break;
45314779705SSam Leffler }
45414779705SSam Leffler
45514779705SSam Leffler OS_REG_WRITE(ah, AR_QMISC(q), qmisc);
45614779705SSam Leffler OS_REG_WRITE(ah, AR_DMISC(q), dmisc);
45714779705SSam Leffler
45814779705SSam Leffler /* Setup compression scratchpad buffer */
45914779705SSam Leffler /*
46014779705SSam Leffler * XXX: calling this asynchronously to queue operation can
46114779705SSam Leffler * cause unexpected behavior!!!
46214779705SSam Leffler */
46314779705SSam Leffler if (qi->tqi_physCompBuf) {
46414779705SSam Leffler HALASSERT(qi->tqi_type == HAL_TX_QUEUE_DATA ||
46514779705SSam Leffler qi->tqi_type == HAL_TX_QUEUE_UAPSD);
46614779705SSam Leffler OS_REG_WRITE(ah, AR_Q_CBBS, (80 + 2*q));
46714779705SSam Leffler OS_REG_WRITE(ah, AR_Q_CBBA, qi->tqi_physCompBuf);
46814779705SSam Leffler OS_REG_WRITE(ah, AR_Q_CBC, HAL_COMP_BUF_MAX_SIZE/1024);
46914779705SSam Leffler OS_REG_WRITE(ah, AR_Q0_MISC + 4*q,
47014779705SSam Leffler OS_REG_READ(ah, AR_Q0_MISC + 4*q)
47114779705SSam Leffler | AR_Q_MISC_QCU_COMP_EN);
47214779705SSam Leffler }
47314779705SSam Leffler
47414779705SSam Leffler /*
47514779705SSam Leffler * Always update the secondary interrupt mask registers - this
47614779705SSam Leffler * could be a new queue getting enabled in a running system or
47714779705SSam Leffler * hw getting re-initialized during a reset!
47814779705SSam Leffler *
47914779705SSam Leffler * Since we don't differentiate between tx interrupts corresponding
48014779705SSam Leffler * to individual queues - secondary tx mask regs are always unmasked;
48114779705SSam Leffler * tx interrupts are enabled/disabled for all queues collectively
48214779705SSam Leffler * using the primary mask reg
48314779705SSam Leffler */
48414779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE)
48514779705SSam Leffler ahp->ah_txOkInterruptMask |= 1 << q;
48614779705SSam Leffler else
48714779705SSam Leffler ahp->ah_txOkInterruptMask &= ~(1 << q);
48814779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE)
48914779705SSam Leffler ahp->ah_txErrInterruptMask |= 1 << q;
49014779705SSam Leffler else
49114779705SSam Leffler ahp->ah_txErrInterruptMask &= ~(1 << q);
49214779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_TXDESCINT_ENABLE)
49314779705SSam Leffler ahp->ah_txDescInterruptMask |= 1 << q;
49414779705SSam Leffler else
49514779705SSam Leffler ahp->ah_txDescInterruptMask &= ~(1 << q);
49614779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE)
49714779705SSam Leffler ahp->ah_txEolInterruptMask |= 1 << q;
49814779705SSam Leffler else
49914779705SSam Leffler ahp->ah_txEolInterruptMask &= ~(1 << q);
50014779705SSam Leffler if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE)
50114779705SSam Leffler ahp->ah_txUrnInterruptMask |= 1 << q;
50214779705SSam Leffler else
50314779705SSam Leffler ahp->ah_txUrnInterruptMask &= ~(1 << q);
50414779705SSam Leffler setTxQInterrupts(ah, qi);
50514779705SSam Leffler
50614779705SSam Leffler return AH_TRUE;
50714779705SSam Leffler }
508353d2977SAdrian Chadd #undef TU_TO_USEC
50914779705SSam Leffler
51014779705SSam Leffler /*
51114779705SSam Leffler * Get the TXDP for the specified queue
51214779705SSam Leffler */
51314779705SSam Leffler uint32_t
ar5212GetTxDP(struct ath_hal * ah,u_int q)51414779705SSam Leffler ar5212GetTxDP(struct ath_hal *ah, u_int q)
51514779705SSam Leffler {
51614779705SSam Leffler HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues);
51714779705SSam Leffler return OS_REG_READ(ah, AR_QTXDP(q));
51814779705SSam Leffler }
51914779705SSam Leffler
52014779705SSam Leffler /*
52114779705SSam Leffler * Set the TxDP for the specified queue
52214779705SSam Leffler */
52314779705SSam Leffler HAL_BOOL
ar5212SetTxDP(struct ath_hal * ah,u_int q,uint32_t txdp)52414779705SSam Leffler ar5212SetTxDP(struct ath_hal *ah, u_int q, uint32_t txdp)
52514779705SSam Leffler {
52614779705SSam Leffler HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues);
52714779705SSam Leffler HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE);
52814779705SSam Leffler
52914779705SSam Leffler /*
53014779705SSam Leffler * Make sure that TXE is deasserted before setting the TXDP. If TXE
53114779705SSam Leffler * is still asserted, setting TXDP will have no effect.
53214779705SSam Leffler */
53314779705SSam Leffler HALASSERT((OS_REG_READ(ah, AR_Q_TXE) & (1 << q)) == 0);
53414779705SSam Leffler
53514779705SSam Leffler OS_REG_WRITE(ah, AR_QTXDP(q), txdp);
53614779705SSam Leffler
53714779705SSam Leffler return AH_TRUE;
53814779705SSam Leffler }
53914779705SSam Leffler
54014779705SSam Leffler /*
54114779705SSam Leffler * Set Transmit Enable bits for the specified queue
54214779705SSam Leffler */
54314779705SSam Leffler HAL_BOOL
ar5212StartTxDma(struct ath_hal * ah,u_int q)54414779705SSam Leffler ar5212StartTxDma(struct ath_hal *ah, u_int q)
54514779705SSam Leffler {
54614779705SSam Leffler HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues);
54714779705SSam Leffler
54814779705SSam Leffler HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE);
54914779705SSam Leffler
55014779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q);
55114779705SSam Leffler
55214779705SSam Leffler /* Check to be sure we're not enabling a q that has its TXD bit set. */
55314779705SSam Leffler HALASSERT((OS_REG_READ(ah, AR_Q_TXD) & (1 << q)) == 0);
55414779705SSam Leffler
55514779705SSam Leffler OS_REG_WRITE(ah, AR_Q_TXE, 1 << q);
55614779705SSam Leffler return AH_TRUE;
55714779705SSam Leffler }
55814779705SSam Leffler
55914779705SSam Leffler /*
56014779705SSam Leffler * Return the number of pending frames or 0 if the specified
56114779705SSam Leffler * queue is stopped.
56214779705SSam Leffler */
56314779705SSam Leffler uint32_t
ar5212NumTxPending(struct ath_hal * ah,u_int q)56414779705SSam Leffler ar5212NumTxPending(struct ath_hal *ah, u_int q)
56514779705SSam Leffler {
56614779705SSam Leffler uint32_t npend;
56714779705SSam Leffler
56814779705SSam Leffler HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues);
56914779705SSam Leffler HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE);
57014779705SSam Leffler
57114779705SSam Leffler npend = OS_REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
57214779705SSam Leffler if (npend == 0) {
57314779705SSam Leffler /*
57414779705SSam Leffler * Pending frame count (PFC) can momentarily go to zero
57514779705SSam Leffler * while TXE remains asserted. In other words a PFC of
57614779705SSam Leffler * zero is not sufficient to say that the queue has stopped.
57714779705SSam Leffler */
57814779705SSam Leffler if (OS_REG_READ(ah, AR_Q_TXE) & (1 << q))
57914779705SSam Leffler npend = 1; /* arbitrarily return 1 */
58014779705SSam Leffler }
58114779705SSam Leffler return npend;
58214779705SSam Leffler }
58314779705SSam Leffler
58414779705SSam Leffler /*
58514779705SSam Leffler * Stop transmit on the specified queue
58614779705SSam Leffler */
58714779705SSam Leffler HAL_BOOL
ar5212StopTxDma(struct ath_hal * ah,u_int q)58814779705SSam Leffler ar5212StopTxDma(struct ath_hal *ah, u_int q)
58914779705SSam Leffler {
59014779705SSam Leffler u_int i;
59114779705SSam Leffler u_int wait;
59214779705SSam Leffler
59314779705SSam Leffler HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues);
59414779705SSam Leffler
59514779705SSam Leffler HALASSERT(AH5212(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE);
59614779705SSam Leffler
59714779705SSam Leffler OS_REG_WRITE(ah, AR_Q_TXD, 1 << q);
59814779705SSam Leffler for (i = 1000; i != 0; i--) {
59914779705SSam Leffler if (ar5212NumTxPending(ah, q) == 0)
60014779705SSam Leffler break;
60114779705SSam Leffler OS_DELAY(100); /* XXX get actual value */
60214779705SSam Leffler }
60314779705SSam Leffler #ifdef AH_DEBUG
60414779705SSam Leffler if (i == 0) {
60514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
60614779705SSam Leffler "%s: queue %u DMA did not stop in 100 msec\n", __func__, q);
60714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
60814779705SSam Leffler "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__,
60914779705SSam Leffler OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE),
61014779705SSam Leffler OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q)));
61114779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
61214779705SSam Leffler "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n",
61314779705SSam Leffler __func__, OS_REG_READ(ah, AR_QMISC(q)),
61414779705SSam Leffler OS_REG_READ(ah, AR_QRDYTIMECFG(q)),
61514779705SSam Leffler OS_REG_READ(ah, AR_Q_RDYTIMESHDN));
61614779705SSam Leffler }
61714779705SSam Leffler #endif /* AH_DEBUG */
61814779705SSam Leffler
61914779705SSam Leffler /* 2413+ and up can kill packets at the PCU level */
62014779705SSam Leffler if (ar5212NumTxPending(ah, q) &&
62114779705SSam Leffler (IS_2413(ah) || IS_5413(ah) || IS_2425(ah) || IS_2417(ah))) {
62214779705SSam Leffler uint32_t tsfLow, j;
62314779705SSam Leffler
62414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
62514779705SSam Leffler "%s: Num of pending TX Frames %d on Q %d\n",
62614779705SSam Leffler __func__, ar5212NumTxPending(ah, q), q);
62714779705SSam Leffler
62814779705SSam Leffler /* Kill last PCU Tx Frame */
62914779705SSam Leffler /* TODO - save off and restore current values of Q1/Q2? */
63014779705SSam Leffler for (j = 0; j < 2; j++) {
63114779705SSam Leffler tsfLow = OS_REG_READ(ah, AR_TSF_L32);
63214779705SSam Leffler OS_REG_WRITE(ah, AR_QUIET2, SM(100, AR_QUIET2_QUIET_PER) |
63314779705SSam Leffler SM(10, AR_QUIET2_QUIET_DUR));
63414779705SSam Leffler OS_REG_WRITE(ah, AR_QUIET1, AR_QUIET1_QUIET_ENABLE |
63514779705SSam Leffler SM(tsfLow >> 10, AR_QUIET1_NEXT_QUIET));
63614779705SSam Leffler if ((OS_REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10)) {
63714779705SSam Leffler break;
63814779705SSam Leffler }
63914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
64014779705SSam Leffler "%s: TSF moved while trying to set quiet time "
64114779705SSam Leffler "TSF: 0x%08x\n", __func__, tsfLow);
64214779705SSam Leffler HALASSERT(j < 1); /* TSF shouldn't count twice or reg access is taking forever */
64314779705SSam Leffler }
64414779705SSam Leffler
64514779705SSam Leffler OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE);
64614779705SSam Leffler
64714779705SSam Leffler /* Allow the quiet mechanism to do its work */
64814779705SSam Leffler OS_DELAY(200);
64914779705SSam Leffler OS_REG_CLR_BIT(ah, AR_QUIET1, AR_QUIET1_QUIET_ENABLE);
65014779705SSam Leffler
65114779705SSam Leffler /* Give at least 1 millisec more to wait */
65214779705SSam Leffler wait = 100;
65314779705SSam Leffler
65414779705SSam Leffler /* Verify all transmit is dead */
65514779705SSam Leffler while (ar5212NumTxPending(ah, q)) {
65614779705SSam Leffler if ((--wait) == 0) {
65714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
65814779705SSam Leffler "%s: Failed to stop Tx DMA in %d msec after killing last frame\n",
65914779705SSam Leffler __func__, wait);
66014779705SSam Leffler break;
66114779705SSam Leffler }
66214779705SSam Leffler OS_DELAY(10);
66314779705SSam Leffler }
66414779705SSam Leffler
66514779705SSam Leffler OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_CHAN_IDLE);
66614779705SSam Leffler }
66714779705SSam Leffler
66814779705SSam Leffler OS_REG_WRITE(ah, AR_Q_TXD, 0);
66914779705SSam Leffler return (i != 0);
67014779705SSam Leffler }
67114779705SSam Leffler
67214779705SSam Leffler /*
67314779705SSam Leffler * Descriptor Access Functions
67414779705SSam Leffler */
67514779705SSam Leffler
67614779705SSam Leffler #define VALID_PKT_TYPES \
67714779705SSam Leffler ((1<<HAL_PKT_TYPE_NORMAL)|(1<<HAL_PKT_TYPE_ATIM)|\
67814779705SSam Leffler (1<<HAL_PKT_TYPE_PSPOLL)|(1<<HAL_PKT_TYPE_PROBE_RESP)|\
67914779705SSam Leffler (1<<HAL_PKT_TYPE_BEACON))
68014779705SSam Leffler #define isValidPktType(_t) ((1<<(_t)) & VALID_PKT_TYPES)
68114779705SSam Leffler #define VALID_TX_RATES \
68214779705SSam Leffler ((1<<0x0b)|(1<<0x0f)|(1<<0x0a)|(1<<0x0e)|(1<<0x09)|(1<<0x0d)|\
68314779705SSam Leffler (1<<0x08)|(1<<0x0c)|(1<<0x1b)|(1<<0x1a)|(1<<0x1e)|(1<<0x19)|\
68414779705SSam Leffler (1<<0x1d)|(1<<0x18)|(1<<0x1c))
68514779705SSam Leffler #define isValidTxRate(_r) ((1<<(_r)) & VALID_TX_RATES)
68614779705SSam Leffler
68714779705SSam Leffler HAL_BOOL
ar5212SetupTxDesc(struct ath_hal * ah,struct ath_desc * ds,u_int pktLen,u_int hdrLen,HAL_PKT_TYPE type,u_int txPower,u_int txRate0,u_int txTries0,u_int keyIx,u_int antMode,u_int flags,u_int rtsctsRate,u_int rtsctsDuration,u_int compicvLen,u_int compivLen,u_int comp)68814779705SSam Leffler ar5212SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds,
68914779705SSam Leffler u_int pktLen,
69014779705SSam Leffler u_int hdrLen,
69114779705SSam Leffler HAL_PKT_TYPE type,
69214779705SSam Leffler u_int txPower,
69314779705SSam Leffler u_int txRate0, u_int txTries0,
69414779705SSam Leffler u_int keyIx,
69514779705SSam Leffler u_int antMode,
69614779705SSam Leffler u_int flags,
69714779705SSam Leffler u_int rtsctsRate,
69814779705SSam Leffler u_int rtsctsDuration,
69914779705SSam Leffler u_int compicvLen,
70014779705SSam Leffler u_int compivLen,
70114779705SSam Leffler u_int comp)
70214779705SSam Leffler {
70314779705SSam Leffler #define RTSCTS (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)
70414779705SSam Leffler struct ar5212_desc *ads = AR5212DESC(ds);
70514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
70614779705SSam Leffler
70714779705SSam Leffler (void) hdrLen;
70814779705SSam Leffler
70914779705SSam Leffler HALASSERT(txTries0 != 0);
71014779705SSam Leffler HALASSERT(isValidPktType(type));
71114779705SSam Leffler HALASSERT(isValidTxRate(txRate0));
71214779705SSam Leffler HALASSERT((flags & RTSCTS) != RTSCTS);
71314779705SSam Leffler /* XXX validate antMode */
71414779705SSam Leffler
71514779705SSam Leffler txPower = (txPower + ahp->ah_txPowerIndexOffset );
71614779705SSam Leffler if(txPower > 63) txPower=63;
71714779705SSam Leffler
71814779705SSam Leffler ads->ds_ctl0 = (pktLen & AR_FrameLen)
71914779705SSam Leffler | (txPower << AR_XmitPower_S)
72014779705SSam Leffler | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0)
72114779705SSam Leffler | (flags & HAL_TXDESC_CLRDMASK ? AR_ClearDestMask : 0)
72214779705SSam Leffler | SM(antMode, AR_AntModeXmit)
72314779705SSam Leffler | (flags & HAL_TXDESC_INTREQ ? AR_TxInterReq : 0)
72414779705SSam Leffler ;
72514779705SSam Leffler ads->ds_ctl1 = (type << AR_FrmType_S)
72614779705SSam Leffler | (flags & HAL_TXDESC_NOACK ? AR_NoAck : 0)
72714779705SSam Leffler | (comp << AR_CompProc_S)
72814779705SSam Leffler | (compicvLen << AR_CompICVLen_S)
72914779705SSam Leffler | (compivLen << AR_CompIVLen_S)
73014779705SSam Leffler ;
73114779705SSam Leffler ads->ds_ctl2 = SM(txTries0, AR_XmitDataTries0)
73214779705SSam Leffler | (flags & HAL_TXDESC_DURENA ? AR_DurUpdateEna : 0)
73314779705SSam Leffler ;
73414779705SSam Leffler ads->ds_ctl3 = (txRate0 << AR_XmitRate0_S)
73514779705SSam Leffler ;
73614779705SSam Leffler if (keyIx != HAL_TXKEYIX_INVALID) {
73714779705SSam Leffler /* XXX validate key index */
73814779705SSam Leffler ads->ds_ctl1 |= SM(keyIx, AR_DestIdx);
73914779705SSam Leffler ads->ds_ctl0 |= AR_DestIdxValid;
74014779705SSam Leffler }
74114779705SSam Leffler if (flags & RTSCTS) {
74214779705SSam Leffler if (!isValidTxRate(rtsctsRate)) {
74314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
74414779705SSam Leffler "%s: invalid rts/cts rate 0x%x\n",
74514779705SSam Leffler __func__, rtsctsRate);
74614779705SSam Leffler return AH_FALSE;
74714779705SSam Leffler }
74814779705SSam Leffler /* XXX validate rtsctsDuration */
74914779705SSam Leffler ads->ds_ctl0 |= (flags & HAL_TXDESC_CTSENA ? AR_CTSEnable : 0)
75014779705SSam Leffler | (flags & HAL_TXDESC_RTSENA ? AR_RTSCTSEnable : 0)
75114779705SSam Leffler ;
75214779705SSam Leffler ads->ds_ctl2 |= SM(rtsctsDuration, AR_RTSCTSDuration);
75314779705SSam Leffler ads->ds_ctl3 |= (rtsctsRate << AR_RTSCTSRate_S);
75414779705SSam Leffler }
75514779705SSam Leffler return AH_TRUE;
75614779705SSam Leffler #undef RTSCTS
75714779705SSam Leffler }
75814779705SSam Leffler
75914779705SSam Leffler HAL_BOOL
ar5212SetupXTxDesc(struct ath_hal * ah,struct ath_desc * ds,u_int txRate1,u_int txTries1,u_int txRate2,u_int txTries2,u_int txRate3,u_int txTries3)76014779705SSam Leffler ar5212SetupXTxDesc(struct ath_hal *ah, struct ath_desc *ds,
76114779705SSam Leffler u_int txRate1, u_int txTries1,
76214779705SSam Leffler u_int txRate2, u_int txTries2,
76314779705SSam Leffler u_int txRate3, u_int txTries3)
76414779705SSam Leffler {
76514779705SSam Leffler struct ar5212_desc *ads = AR5212DESC(ds);
76614779705SSam Leffler
76714779705SSam Leffler if (txTries1) {
76814779705SSam Leffler HALASSERT(isValidTxRate(txRate1));
76914779705SSam Leffler ads->ds_ctl2 |= SM(txTries1, AR_XmitDataTries1)
77014779705SSam Leffler | AR_DurUpdateEna
77114779705SSam Leffler ;
77214779705SSam Leffler ads->ds_ctl3 |= (txRate1 << AR_XmitRate1_S);
77314779705SSam Leffler }
77414779705SSam Leffler if (txTries2) {
77514779705SSam Leffler HALASSERT(isValidTxRate(txRate2));
77614779705SSam Leffler ads->ds_ctl2 |= SM(txTries2, AR_XmitDataTries2)
77714779705SSam Leffler | AR_DurUpdateEna
77814779705SSam Leffler ;
77914779705SSam Leffler ads->ds_ctl3 |= (txRate2 << AR_XmitRate2_S);
78014779705SSam Leffler }
78114779705SSam Leffler if (txTries3) {
78214779705SSam Leffler HALASSERT(isValidTxRate(txRate3));
78314779705SSam Leffler ads->ds_ctl2 |= SM(txTries3, AR_XmitDataTries3)
78414779705SSam Leffler | AR_DurUpdateEna
78514779705SSam Leffler ;
78614779705SSam Leffler ads->ds_ctl3 |= (txRate3 << AR_XmitRate3_S);
78714779705SSam Leffler }
78814779705SSam Leffler return AH_TRUE;
78914779705SSam Leffler }
79014779705SSam Leffler
79114779705SSam Leffler void
ar5212IntrReqTxDesc(struct ath_hal * ah,struct ath_desc * ds)79214779705SSam Leffler ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *ds)
79314779705SSam Leffler {
79414779705SSam Leffler struct ar5212_desc *ads = AR5212DESC(ds);
79514779705SSam Leffler
79614779705SSam Leffler #ifdef AH_NEED_DESC_SWAP
79714779705SSam Leffler ads->ds_ctl0 |= __bswap32(AR_TxInterReq);
79814779705SSam Leffler #else
79914779705SSam Leffler ads->ds_ctl0 |= AR_TxInterReq;
80014779705SSam Leffler #endif
80114779705SSam Leffler }
80214779705SSam Leffler
80314779705SSam Leffler HAL_BOOL
ar5212FillTxDesc(struct ath_hal * ah,struct ath_desc * ds,HAL_DMA_ADDR * bufAddrList,uint32_t * segLenList,u_int qcuId,u_int descId,HAL_BOOL firstSeg,HAL_BOOL lastSeg,const struct ath_desc * ds0)80414779705SSam Leffler ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
80546634305SAdrian Chadd HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int qcuId,
80646634305SAdrian Chadd u_int descId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
80714779705SSam Leffler const struct ath_desc *ds0)
80814779705SSam Leffler {
80914779705SSam Leffler struct ar5212_desc *ads = AR5212DESC(ds);
81046634305SAdrian Chadd uint32_t segLen = segLenList[0];
81114779705SSam Leffler
81214779705SSam Leffler HALASSERT((segLen &~ AR_BufLen) == 0);
81314779705SSam Leffler
81446634305SAdrian Chadd ds->ds_data = bufAddrList[0];
81546634305SAdrian Chadd
81614779705SSam Leffler if (firstSeg) {
81714779705SSam Leffler /*
81814779705SSam Leffler * First descriptor, don't clobber xmit control data
81914779705SSam Leffler * setup by ar5212SetupTxDesc.
82014779705SSam Leffler */
82114779705SSam Leffler ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_More);
82214779705SSam Leffler } else if (lastSeg) { /* !firstSeg && lastSeg */
82314779705SSam Leffler /*
82414779705SSam Leffler * Last descriptor in a multi-descriptor frame,
82514779705SSam Leffler * copy the multi-rate transmit parameters from
82614779705SSam Leffler * the first frame for processing on completion.
82714779705SSam Leffler */
82814779705SSam Leffler ads->ds_ctl1 = segLen;
82914779705SSam Leffler #ifdef AH_NEED_DESC_SWAP
830e674b5f3SAdrian Chadd ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0)
831e674b5f3SAdrian Chadd & AR_TxInterReq;
83214779705SSam Leffler ads->ds_ctl2 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl2);
83314779705SSam Leffler ads->ds_ctl3 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl3);
83414779705SSam Leffler #else
835e674b5f3SAdrian Chadd ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq;
83614779705SSam Leffler ads->ds_ctl2 = AR5212DESC_CONST(ds0)->ds_ctl2;
83714779705SSam Leffler ads->ds_ctl3 = AR5212DESC_CONST(ds0)->ds_ctl3;
83814779705SSam Leffler #endif
83914779705SSam Leffler } else { /* !firstSeg && !lastSeg */
84014779705SSam Leffler /*
84114779705SSam Leffler * Intermediate descriptor in a multi-descriptor frame.
84214779705SSam Leffler */
843e674b5f3SAdrian Chadd #ifdef AH_NEED_DESC_SWAP
844e674b5f3SAdrian Chadd ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0)
845e674b5f3SAdrian Chadd & AR_TxInterReq;
846e674b5f3SAdrian Chadd #else
847e674b5f3SAdrian Chadd ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq;
848e674b5f3SAdrian Chadd #endif
84914779705SSam Leffler ads->ds_ctl1 = segLen | AR_More;
85014779705SSam Leffler ads->ds_ctl2 = 0;
85114779705SSam Leffler ads->ds_ctl3 = 0;
85214779705SSam Leffler }
85314779705SSam Leffler ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
85414779705SSam Leffler return AH_TRUE;
85514779705SSam Leffler }
85614779705SSam Leffler
85714779705SSam Leffler #ifdef AH_NEED_DESC_SWAP
85814779705SSam Leffler /* Swap transmit descriptor */
85914779705SSam Leffler static __inline void
ar5212SwapTxDesc(struct ath_desc * ds)86014779705SSam Leffler ar5212SwapTxDesc(struct ath_desc *ds)
86114779705SSam Leffler {
86214779705SSam Leffler ds->ds_data = __bswap32(ds->ds_data);
86314779705SSam Leffler ds->ds_ctl0 = __bswap32(ds->ds_ctl0);
86414779705SSam Leffler ds->ds_ctl1 = __bswap32(ds->ds_ctl1);
86514779705SSam Leffler ds->ds_hw[0] = __bswap32(ds->ds_hw[0]);
86614779705SSam Leffler ds->ds_hw[1] = __bswap32(ds->ds_hw[1]);
86714779705SSam Leffler ds->ds_hw[2] = __bswap32(ds->ds_hw[2]);
86814779705SSam Leffler ds->ds_hw[3] = __bswap32(ds->ds_hw[3]);
86914779705SSam Leffler }
87014779705SSam Leffler #endif
87114779705SSam Leffler
87214779705SSam Leffler /*
87314779705SSam Leffler * Processing of HW TX descriptor.
87414779705SSam Leffler */
87514779705SSam Leffler HAL_STATUS
ar5212ProcTxDesc(struct ath_hal * ah,struct ath_desc * ds,struct ath_tx_status * ts)87614779705SSam Leffler ar5212ProcTxDesc(struct ath_hal *ah,
87714779705SSam Leffler struct ath_desc *ds, struct ath_tx_status *ts)
87814779705SSam Leffler {
87914779705SSam Leffler struct ar5212_desc *ads = AR5212DESC(ds);
88014779705SSam Leffler
88114779705SSam Leffler #ifdef AH_NEED_DESC_SWAP
88214779705SSam Leffler if ((ads->ds_txstatus1 & __bswap32(AR_Done)) == 0)
88314779705SSam Leffler return HAL_EINPROGRESS;
88414779705SSam Leffler
88514779705SSam Leffler ar5212SwapTxDesc(ds);
88614779705SSam Leffler #else
88714779705SSam Leffler if ((ads->ds_txstatus1 & AR_Done) == 0)
88814779705SSam Leffler return HAL_EINPROGRESS;
88914779705SSam Leffler #endif
89014779705SSam Leffler
89114779705SSam Leffler /* Update software copies of the HW status */
89214779705SSam Leffler ts->ts_seqnum = MS(ads->ds_txstatus1, AR_SeqNum);
89314779705SSam Leffler ts->ts_tstamp = MS(ads->ds_txstatus0, AR_SendTimestamp);
89414779705SSam Leffler ts->ts_status = 0;
89514779705SSam Leffler if ((ads->ds_txstatus0 & AR_FrmXmitOK) == 0) {
89614779705SSam Leffler if (ads->ds_txstatus0 & AR_ExcessiveRetries)
89714779705SSam Leffler ts->ts_status |= HAL_TXERR_XRETRY;
89814779705SSam Leffler if (ads->ds_txstatus0 & AR_Filtered)
89914779705SSam Leffler ts->ts_status |= HAL_TXERR_FILT;
90014779705SSam Leffler if (ads->ds_txstatus0 & AR_FIFOUnderrun)
90114779705SSam Leffler ts->ts_status |= HAL_TXERR_FIFO;
90214779705SSam Leffler }
90314779705SSam Leffler /*
90414779705SSam Leffler * Extract the transmit rate used and mark the rate as
90514779705SSam Leffler * ``alternate'' if it wasn't the series 0 rate.
90614779705SSam Leffler */
90714779705SSam Leffler ts->ts_finaltsi = MS(ads->ds_txstatus1, AR_FinalTSIndex);
90814779705SSam Leffler switch (ts->ts_finaltsi) {
90914779705SSam Leffler case 0:
91014779705SSam Leffler ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate0);
91114779705SSam Leffler break;
91214779705SSam Leffler case 1:
913f6cbf16aSSam Leffler ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate1);
91414779705SSam Leffler break;
91514779705SSam Leffler case 2:
916f6cbf16aSSam Leffler ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate2);
91714779705SSam Leffler break;
91814779705SSam Leffler case 3:
919f6cbf16aSSam Leffler ts->ts_rate = MS(ads->ds_ctl3, AR_XmitRate3);
92014779705SSam Leffler break;
92114779705SSam Leffler }
92214779705SSam Leffler ts->ts_rssi = MS(ads->ds_txstatus1, AR_AckSigStrength);
92314779705SSam Leffler ts->ts_shortretry = MS(ads->ds_txstatus0, AR_RTSFailCnt);
92414779705SSam Leffler ts->ts_longretry = MS(ads->ds_txstatus0, AR_DataFailCnt);
92514779705SSam Leffler /*
92614779705SSam Leffler * The retry count has the number of un-acked tries for the
92714779705SSam Leffler * final series used. When doing multi-rate retry we must
92814779705SSam Leffler * fixup the retry count by adding in the try counts for
92914779705SSam Leffler * each series that was fully-processed. Beware that this
93014779705SSam Leffler * takes values from the try counts in the final descriptor.
93114779705SSam Leffler * These are not required by the hardware. We assume they
93214779705SSam Leffler * are placed there by the driver as otherwise we have no
93314779705SSam Leffler * access and the driver can't do the calculation because it
93414779705SSam Leffler * doesn't know the descriptor format.
93514779705SSam Leffler */
93614779705SSam Leffler switch (ts->ts_finaltsi) {
93714779705SSam Leffler case 3: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries2);
93814779705SSam Leffler case 2: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries1);
93914779705SSam Leffler case 1: ts->ts_longretry += MS(ads->ds_ctl2, AR_XmitDataTries0);
94014779705SSam Leffler }
94114779705SSam Leffler ts->ts_virtcol = MS(ads->ds_txstatus0, AR_VirtCollCnt);
94214779705SSam Leffler ts->ts_antenna = (ads->ds_txstatus1 & AR_XmitAtenna ? 2 : 1);
94314779705SSam Leffler
94414779705SSam Leffler return HAL_OK;
94514779705SSam Leffler }
94614779705SSam Leffler
94714779705SSam Leffler /*
94814779705SSam Leffler * Determine which tx queues need interrupt servicing.
94914779705SSam Leffler */
95014779705SSam Leffler void
ar5212GetTxIntrQueue(struct ath_hal * ah,uint32_t * txqs)95114779705SSam Leffler ar5212GetTxIntrQueue(struct ath_hal *ah, uint32_t *txqs)
95214779705SSam Leffler {
95314779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
95414779705SSam Leffler *txqs &= ahp->ah_intrTxqs;
95514779705SSam Leffler ahp->ah_intrTxqs &= ~(*txqs);
95614779705SSam Leffler }
9579ea46744SAdrian Chadd
9589ea46744SAdrian Chadd /*
9599ea46744SAdrian Chadd * Retrieve the rate table from the given TX completion descriptor
9609ea46744SAdrian Chadd */
9619ea46744SAdrian Chadd HAL_BOOL
ar5212GetTxCompletionRates(struct ath_hal * ah,const struct ath_desc * ds0,int * rates,int * tries)9629ea46744SAdrian Chadd ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries)
9639ea46744SAdrian Chadd {
9649ea46744SAdrian Chadd const struct ar5212_desc *ads = AR5212DESC_CONST(ds0);
9659ea46744SAdrian Chadd
9669ea46744SAdrian Chadd rates[0] = MS(ads->ds_ctl3, AR_XmitRate0);
9679ea46744SAdrian Chadd rates[1] = MS(ads->ds_ctl3, AR_XmitRate1);
9689ea46744SAdrian Chadd rates[2] = MS(ads->ds_ctl3, AR_XmitRate2);
9699ea46744SAdrian Chadd rates[3] = MS(ads->ds_ctl3, AR_XmitRate3);
9709ea46744SAdrian Chadd
9719ea46744SAdrian Chadd tries[0] = MS(ads->ds_ctl2, AR_XmitDataTries0);
9729ea46744SAdrian Chadd tries[1] = MS(ads->ds_ctl2, AR_XmitDataTries1);
9739ea46744SAdrian Chadd tries[2] = MS(ads->ds_ctl2, AR_XmitDataTries2);
9749ea46744SAdrian Chadd tries[3] = MS(ads->ds_ctl2, AR_XmitDataTries3);
9759ea46744SAdrian Chadd
9769ea46744SAdrian Chadd return AH_TRUE;
9779ea46744SAdrian Chadd }
978ad3e6dcdSAdrian Chadd
979ad3e6dcdSAdrian Chadd void
ar5212SetTxDescLink(struct ath_hal * ah,void * ds,uint32_t link)980ad3e6dcdSAdrian Chadd ar5212SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link)
981ad3e6dcdSAdrian Chadd {
982ad3e6dcdSAdrian Chadd struct ar5212_desc *ads = AR5212DESC(ds);
983ad3e6dcdSAdrian Chadd
984ad3e6dcdSAdrian Chadd ads->ds_link = link;
985ad3e6dcdSAdrian Chadd }
986ad3e6dcdSAdrian Chadd
987ad3e6dcdSAdrian Chadd void
ar5212GetTxDescLink(struct ath_hal * ah,void * ds,uint32_t * link)988ad3e6dcdSAdrian Chadd ar5212GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link)
989ad3e6dcdSAdrian Chadd {
990ad3e6dcdSAdrian Chadd struct ar5212_desc *ads = AR5212DESC(ds);
991ad3e6dcdSAdrian Chadd
992ad3e6dcdSAdrian Chadd *link = ads->ds_link;
993ad3e6dcdSAdrian Chadd }
994ad3e6dcdSAdrian Chadd
995ad3e6dcdSAdrian Chadd void
ar5212GetTxDescLinkPtr(struct ath_hal * ah,void * ds,uint32_t ** linkptr)996ad3e6dcdSAdrian Chadd ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr)
997ad3e6dcdSAdrian Chadd {
998ad3e6dcdSAdrian Chadd struct ar5212_desc *ads = AR5212DESC(ds);
999ad3e6dcdSAdrian Chadd
1000ad3e6dcdSAdrian Chadd *linkptr = &ads->ds_link;
1001ad3e6dcdSAdrian Chadd }
1002