13fdfc330SAdrian Chadd /*- 2*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*718cf2ccSPedro F. Giffuni * 43fdfc330SAdrian Chadd * Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org> 53fdfc330SAdrian Chadd * All rights reserved. 63fdfc330SAdrian Chadd * 73fdfc330SAdrian Chadd * Redistribution and use in source and binary forms, with or without 83fdfc330SAdrian Chadd * modification, are permitted provided that the following conditions 93fdfc330SAdrian Chadd * are met: 103fdfc330SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 113fdfc330SAdrian Chadd * notice, this list of conditions and the following disclaimer, 123fdfc330SAdrian Chadd * without modification. 133fdfc330SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 143fdfc330SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 153fdfc330SAdrian Chadd * redistribution must be conditioned upon including a substantially 163fdfc330SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 173fdfc330SAdrian Chadd * 183fdfc330SAdrian Chadd * NO WARRANTY 193fdfc330SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 203fdfc330SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 213fdfc330SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 223fdfc330SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 233fdfc330SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 243fdfc330SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 253fdfc330SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 263fdfc330SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 273fdfc330SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 283fdfc330SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 293fdfc330SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 303fdfc330SAdrian Chadd */ 313fdfc330SAdrian Chadd 323fdfc330SAdrian Chadd #include <sys/cdefs.h> 333fdfc330SAdrian Chadd __FBSDID("$FreeBSD$"); 343fdfc330SAdrian Chadd 353fdfc330SAdrian Chadd /* 363fdfc330SAdrian Chadd * Driver for the Atheros Wireless LAN controller. 373fdfc330SAdrian Chadd * 383fdfc330SAdrian Chadd * This software is derived from work of Atsushi Onoe; his contribution 393fdfc330SAdrian Chadd * is greatly appreciated. 403fdfc330SAdrian Chadd */ 413fdfc330SAdrian Chadd 423fdfc330SAdrian Chadd #include "opt_inet.h" 433fdfc330SAdrian Chadd #include "opt_ath.h" 443fdfc330SAdrian Chadd /* 453fdfc330SAdrian Chadd * This is needed for register operations which are performed 463fdfc330SAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 473fdfc330SAdrian Chadd * 483fdfc330SAdrian Chadd * It's also required for any AH_DEBUG checks in here, eg the 493fdfc330SAdrian Chadd * module dependencies. 503fdfc330SAdrian Chadd */ 513fdfc330SAdrian Chadd #include "opt_ah.h" 523fdfc330SAdrian Chadd #include "opt_wlan.h" 533fdfc330SAdrian Chadd 543fdfc330SAdrian Chadd #include <sys/param.h> 553fdfc330SAdrian Chadd #include <sys/systm.h> 563fdfc330SAdrian Chadd #include <sys/sysctl.h> 573fdfc330SAdrian Chadd #include <sys/mbuf.h> 583fdfc330SAdrian Chadd #include <sys/malloc.h> 593fdfc330SAdrian Chadd #include <sys/lock.h> 603fdfc330SAdrian Chadd #include <sys/mutex.h> 613fdfc330SAdrian Chadd #include <sys/kernel.h> 623fdfc330SAdrian Chadd #include <sys/socket.h> 633fdfc330SAdrian Chadd #include <sys/sockio.h> 643fdfc330SAdrian Chadd #include <sys/errno.h> 653fdfc330SAdrian Chadd #include <sys/callout.h> 663fdfc330SAdrian Chadd #include <sys/bus.h> 673fdfc330SAdrian Chadd #include <sys/endian.h> 683fdfc330SAdrian Chadd #include <sys/kthread.h> 693fdfc330SAdrian Chadd #include <sys/taskqueue.h> 703fdfc330SAdrian Chadd #include <sys/priv.h> 713fdfc330SAdrian Chadd #include <sys/module.h> 723fdfc330SAdrian Chadd #include <sys/ktr.h> 733fdfc330SAdrian Chadd #include <sys/smp.h> /* for mp_ncpus */ 743fdfc330SAdrian Chadd 753fdfc330SAdrian Chadd #include <machine/bus.h> 763fdfc330SAdrian Chadd 773fdfc330SAdrian Chadd #include <net/if.h> 7876039bc8SGleb Smirnoff #include <net/if_var.h> 793fdfc330SAdrian Chadd #include <net/if_dl.h> 803fdfc330SAdrian Chadd #include <net/if_media.h> 813fdfc330SAdrian Chadd #include <net/if_types.h> 823fdfc330SAdrian Chadd #include <net/if_arp.h> 833fdfc330SAdrian Chadd #include <net/ethernet.h> 843fdfc330SAdrian Chadd #include <net/if_llc.h> 853fdfc330SAdrian Chadd 863fdfc330SAdrian Chadd #include <net80211/ieee80211_var.h> 873fdfc330SAdrian Chadd #include <net80211/ieee80211_regdomain.h> 883fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 893fdfc330SAdrian Chadd #include <net80211/ieee80211_superg.h> 903fdfc330SAdrian Chadd #endif 913fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA 923fdfc330SAdrian Chadd #include <net80211/ieee80211_tdma.h> 933fdfc330SAdrian Chadd #endif 943fdfc330SAdrian Chadd 953fdfc330SAdrian Chadd #include <net/bpf.h> 963fdfc330SAdrian Chadd 973fdfc330SAdrian Chadd #ifdef INET 983fdfc330SAdrian Chadd #include <netinet/in.h> 993fdfc330SAdrian Chadd #include <netinet/if_ether.h> 1003fdfc330SAdrian Chadd #endif 1013fdfc330SAdrian Chadd 1023fdfc330SAdrian Chadd #include <dev/ath/if_athvar.h> 1033fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ 1043fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h> 1053fdfc330SAdrian Chadd 1063fdfc330SAdrian Chadd #include <dev/ath/if_ath_debug.h> 1073fdfc330SAdrian Chadd #include <dev/ath/if_ath_misc.h> 1083fdfc330SAdrian Chadd #include <dev/ath/if_ath_tsf.h> 1093fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx.h> 1103fdfc330SAdrian Chadd #include <dev/ath/if_ath_sysctl.h> 1113fdfc330SAdrian Chadd #include <dev/ath/if_ath_led.h> 1123fdfc330SAdrian Chadd #include <dev/ath/if_ath_keycache.h> 1133fdfc330SAdrian Chadd #include <dev/ath/if_ath_rx.h> 1143fdfc330SAdrian Chadd #include <dev/ath/if_ath_beacon.h> 1153fdfc330SAdrian Chadd #include <dev/ath/if_athdfs.h> 116b45de1ebSAdrian Chadd #include <dev/ath/if_ath_descdma.h> 1173fdfc330SAdrian Chadd 1183fdfc330SAdrian Chadd #ifdef ATH_TX99_DIAG 1193fdfc330SAdrian Chadd #include <dev/ath/ath_tx99/ath_tx99.h> 1203fdfc330SAdrian Chadd #endif 1213fdfc330SAdrian Chadd 1223fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx_edma.h> 1233fdfc330SAdrian Chadd 124b69b0dccSAdrian Chadd #ifdef ATH_DEBUG_ALQ 125b69b0dccSAdrian Chadd #include <dev/ath/if_ath_alq.h> 126b69b0dccSAdrian Chadd #endif 127b69b0dccSAdrian Chadd 1283fdfc330SAdrian Chadd /* 1293fdfc330SAdrian Chadd * some general macros 1303fdfc330SAdrian Chadd */ 1313fdfc330SAdrian Chadd #define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) 1323fdfc330SAdrian Chadd #define DECR(_l, _sz) (_l) --; (_l) &= ((_sz) - 1) 1333fdfc330SAdrian Chadd 134ba3fd9d8SAdrian Chadd /* 135ba3fd9d8SAdrian Chadd * XXX doesn't belong here, and should be tunable 136ba3fd9d8SAdrian Chadd */ 137ba3fd9d8SAdrian Chadd #define ATH_TXSTATUS_RING_SIZE 512 138ba3fd9d8SAdrian Chadd 1393fdfc330SAdrian Chadd MALLOC_DECLARE(M_ATHDEV); 1403fdfc330SAdrian Chadd 141ae3815fdSAdrian Chadd static void ath_edma_tx_processq(struct ath_softc *sc, int dosched); 142ae3815fdSAdrian Chadd 14349236b4eSAdrian Chadd #ifdef ATH_DEBUG_ALQ 14449236b4eSAdrian Chadd static void 14549236b4eSAdrian Chadd ath_tx_alq_edma_push(struct ath_softc *sc, int txq, int nframes, 14649236b4eSAdrian Chadd int fifo_depth, int frame_cnt) 14749236b4eSAdrian Chadd { 14849236b4eSAdrian Chadd struct if_ath_alq_tx_fifo_push aq; 14949236b4eSAdrian Chadd 15049236b4eSAdrian Chadd aq.txq = htobe32(txq); 15149236b4eSAdrian Chadd aq.nframes = htobe32(nframes); 15249236b4eSAdrian Chadd aq.fifo_depth = htobe32(fifo_depth); 15349236b4eSAdrian Chadd aq.frame_cnt = htobe32(frame_cnt); 15449236b4eSAdrian Chadd 15549236b4eSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TX_FIFO_PUSH, 15649236b4eSAdrian Chadd sizeof(aq), 15749236b4eSAdrian Chadd (const char *) &aq); 15849236b4eSAdrian Chadd } 15949236b4eSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 16049236b4eSAdrian Chadd 1614f5ec72aSAdrian Chadd /* 1624f5ec72aSAdrian Chadd * XXX TODO: push an aggregate as a single FIFO slot, even though 1634f5ec72aSAdrian Chadd * it may not meet the TXOP for say, DBA-gated traffic in TDMA mode. 1644f5ec72aSAdrian Chadd * 1654f5ec72aSAdrian Chadd * The TX completion code handles a TX FIFO slot having multiple frames, 1664f5ec72aSAdrian Chadd * aggregate or otherwise, but it may just make things easier to deal 1674f5ec72aSAdrian Chadd * with. 1684f5ec72aSAdrian Chadd * 1694f5ec72aSAdrian Chadd * XXX TODO: track the number of aggregate subframes and put that in the 1704f5ec72aSAdrian Chadd * push alq message. 1714f5ec72aSAdrian Chadd */ 17249236b4eSAdrian Chadd static void 17349236b4eSAdrian Chadd ath_tx_edma_push_staging_list(struct ath_softc *sc, struct ath_txq *txq, 17449236b4eSAdrian Chadd int limit) 17549236b4eSAdrian Chadd { 17649236b4eSAdrian Chadd struct ath_buf *bf, *bf_last; 17749236b4eSAdrian Chadd struct ath_buf *bfi, *bfp; 17849236b4eSAdrian Chadd int i, sqdepth; 17949236b4eSAdrian Chadd TAILQ_HEAD(axq_q_f_s, ath_buf) sq; 18049236b4eSAdrian Chadd 18149236b4eSAdrian Chadd ATH_TXQ_LOCK_ASSERT(txq); 18249236b4eSAdrian Chadd 1836eb9f206SAdrian Chadd DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_TX_PROC, 1846eb9f206SAdrian Chadd "%s: called; TXQ=%d, fifo.depth=%d, axq_q empty=%d\n", 1856eb9f206SAdrian Chadd __func__, 1866eb9f206SAdrian Chadd txq->axq_qnum, 1876eb9f206SAdrian Chadd txq->axq_fifo_depth, 1886eb9f206SAdrian Chadd !! (TAILQ_EMPTY(&txq->axq_q))); 1896eb9f206SAdrian Chadd 19049236b4eSAdrian Chadd /* 19149236b4eSAdrian Chadd * Don't bother doing any work if it's full. 19249236b4eSAdrian Chadd */ 19349236b4eSAdrian Chadd if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) 19449236b4eSAdrian Chadd return; 19549236b4eSAdrian Chadd 19649236b4eSAdrian Chadd if (TAILQ_EMPTY(&txq->axq_q)) 19749236b4eSAdrian Chadd return; 19849236b4eSAdrian Chadd 19949236b4eSAdrian Chadd TAILQ_INIT(&sq); 20049236b4eSAdrian Chadd 20149236b4eSAdrian Chadd /* 20249236b4eSAdrian Chadd * First pass - walk sq, queue up to 'limit' entries, 20349236b4eSAdrian Chadd * subtract them from the staging queue. 20449236b4eSAdrian Chadd */ 20549236b4eSAdrian Chadd sqdepth = 0; 20649236b4eSAdrian Chadd for (i = 0; i < limit; i++) { 20749236b4eSAdrian Chadd /* Grab the head entry */ 20849236b4eSAdrian Chadd bf = ATH_TXQ_FIRST(txq); 20949236b4eSAdrian Chadd if (bf == NULL) 21049236b4eSAdrian Chadd break; 21149236b4eSAdrian Chadd ATH_TXQ_REMOVE(txq, bf, bf_list); 21249236b4eSAdrian Chadd 21349236b4eSAdrian Chadd /* Queue it into our staging list */ 21449236b4eSAdrian Chadd TAILQ_INSERT_TAIL(&sq, bf, bf_list); 2151a9bf047SAdrian Chadd 2161a9bf047SAdrian Chadd /* Ensure the flags are cleared */ 2171a9bf047SAdrian Chadd bf->bf_flags &= ~(ATH_BUF_FIFOPTR | ATH_BUF_FIFOEND); 21849236b4eSAdrian Chadd sqdepth++; 21949236b4eSAdrian Chadd } 22049236b4eSAdrian Chadd 22149236b4eSAdrian Chadd /* 22249236b4eSAdrian Chadd * Ok, so now we have a staging list of up to 'limit' 22349236b4eSAdrian Chadd * frames from the txq. Now let's wrap that up 22449236b4eSAdrian Chadd * into its own list and pass that to the hardware 22549236b4eSAdrian Chadd * as one FIFO entry. 22649236b4eSAdrian Chadd */ 22749236b4eSAdrian Chadd 22849236b4eSAdrian Chadd bf = TAILQ_FIRST(&sq); 22949236b4eSAdrian Chadd bf_last = TAILQ_LAST(&sq, axq_q_s); 23049236b4eSAdrian Chadd 23149236b4eSAdrian Chadd /* 23249236b4eSAdrian Chadd * Ok, so here's the gymnastics reqiured to make this 23349236b4eSAdrian Chadd * all sensible. 23449236b4eSAdrian Chadd */ 23549236b4eSAdrian Chadd 23649236b4eSAdrian Chadd /* 23749236b4eSAdrian Chadd * Tag the first/last buffer appropriately. 23849236b4eSAdrian Chadd */ 23949236b4eSAdrian Chadd bf->bf_flags |= ATH_BUF_FIFOPTR; 24049236b4eSAdrian Chadd bf_last->bf_flags |= ATH_BUF_FIFOEND; 24149236b4eSAdrian Chadd 24249236b4eSAdrian Chadd /* 24349236b4eSAdrian Chadd * Walk the descriptor list and link them appropriately. 24449236b4eSAdrian Chadd */ 24549236b4eSAdrian Chadd bfp = NULL; 24649236b4eSAdrian Chadd TAILQ_FOREACH(bfi, &sq, bf_list) { 24749236b4eSAdrian Chadd if (bfp != NULL) { 24849236b4eSAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, bfp->bf_lastds, 24949236b4eSAdrian Chadd bfi->bf_daddr); 25049236b4eSAdrian Chadd } 25149236b4eSAdrian Chadd bfp = bfi; 25249236b4eSAdrian Chadd } 25349236b4eSAdrian Chadd 25449236b4eSAdrian Chadd i = 0; 25549236b4eSAdrian Chadd TAILQ_FOREACH(bfi, &sq, bf_list) { 25649236b4eSAdrian Chadd #ifdef ATH_DEBUG 25749236b4eSAdrian Chadd if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 25849236b4eSAdrian Chadd ath_printtxbuf(sc, bfi, txq->axq_qnum, i, 0); 25949236b4eSAdrian Chadd #endif/* ATH_DEBUG */ 26049236b4eSAdrian Chadd #ifdef ATH_DEBUG_ALQ 26149236b4eSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 26249236b4eSAdrian Chadd ath_tx_alq_post(sc, bfi); 26349236b4eSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 26449236b4eSAdrian Chadd i++; 26549236b4eSAdrian Chadd } 26649236b4eSAdrian Chadd 26749236b4eSAdrian Chadd /* 26849236b4eSAdrian Chadd * We now need to push this set of frames onto the tail 26949236b4eSAdrian Chadd * of the FIFO queue. We don't adjust the aggregate 27049236b4eSAdrian Chadd * count, only the queue depth counter(s). 27149236b4eSAdrian Chadd * We also need to blank the link pointer now. 27249236b4eSAdrian Chadd */ 27349236b4eSAdrian Chadd 27449236b4eSAdrian Chadd TAILQ_CONCAT(&txq->fifo.axq_q, &sq, bf_list); 27549236b4eSAdrian Chadd /* Bump total queue tracking in FIFO queue */ 27649236b4eSAdrian Chadd txq->fifo.axq_depth += sqdepth; 27749236b4eSAdrian Chadd 27849236b4eSAdrian Chadd /* Bump FIFO queue */ 27949236b4eSAdrian Chadd txq->axq_fifo_depth++; 280bd3b3362SAdrian Chadd DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_TX_PROC, 28149236b4eSAdrian Chadd "%s: queued %d packets; depth=%d, fifo depth=%d\n", 28249236b4eSAdrian Chadd __func__, sqdepth, txq->fifo.axq_depth, txq->axq_fifo_depth); 28349236b4eSAdrian Chadd 28449236b4eSAdrian Chadd /* Push the first entry into the hardware */ 28549236b4eSAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); 28649236b4eSAdrian Chadd 28749236b4eSAdrian Chadd /* Push start on the DMA if it's not already started */ 28849236b4eSAdrian Chadd ath_hal_txstart(sc->sc_ah, txq->axq_qnum); 28949236b4eSAdrian Chadd 29049236b4eSAdrian Chadd #ifdef ATH_DEBUG_ALQ 29149236b4eSAdrian Chadd ath_tx_alq_edma_push(sc, txq->axq_qnum, sqdepth, 29249236b4eSAdrian Chadd txq->axq_fifo_depth, 29349236b4eSAdrian Chadd txq->fifo.axq_depth); 29449236b4eSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 29549236b4eSAdrian Chadd } 29649236b4eSAdrian Chadd 2974f5ec72aSAdrian Chadd #define TX_BATCH_SIZE 32 2984f5ec72aSAdrian Chadd 29992e84e43SAdrian Chadd /* 30092e84e43SAdrian Chadd * Push some frames into the TX FIFO if we have space. 30192e84e43SAdrian Chadd */ 3024aa8818bSAdrian Chadd static void 3034aa8818bSAdrian Chadd ath_edma_tx_fifo_fill(struct ath_softc *sc, struct ath_txq *txq) 3044aa8818bSAdrian Chadd { 3054aa8818bSAdrian Chadd 306b837332dSAdrian Chadd ATH_TXQ_LOCK_ASSERT(txq); 3074aa8818bSAdrian Chadd 308bd3b3362SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, 309bd3b3362SAdrian Chadd "%s: Q%d: called; fifo.depth=%d, fifo depth=%d, depth=%d, aggr_depth=%d\n", 31092e84e43SAdrian Chadd __func__, 311bd3b3362SAdrian Chadd txq->axq_qnum, 312bd3b3362SAdrian Chadd txq->fifo.axq_depth, 313bd3b3362SAdrian Chadd txq->axq_fifo_depth, 314bd3b3362SAdrian Chadd txq->axq_depth, 315bd3b3362SAdrian Chadd txq->axq_aggr_depth); 3164aa8818bSAdrian Chadd 31792e84e43SAdrian Chadd /* 318bd3b3362SAdrian Chadd * For now, push up to 32 frames per TX FIFO slot. 31949236b4eSAdrian Chadd * If more are in the hardware queue then they'll 32049236b4eSAdrian Chadd * get populated when we try to send another frame 32149236b4eSAdrian Chadd * or complete a frame - so at most there'll be 322bd3b3362SAdrian Chadd * 32 non-AMPDU frames per node/TID anyway. 32392e84e43SAdrian Chadd * 32449236b4eSAdrian Chadd * Note that the hardware staging queue will limit 32549236b4eSAdrian Chadd * how many frames in total we will have pushed into 32649236b4eSAdrian Chadd * here. 32749236b4eSAdrian Chadd * 32849236b4eSAdrian Chadd * Later on, we'll want to push less frames into 32949236b4eSAdrian Chadd * the TX FIFO since we don't want to necessarily 33049236b4eSAdrian Chadd * fill tens or hundreds of milliseconds of potential 33149236b4eSAdrian Chadd * frames. 33249236b4eSAdrian Chadd * 33349236b4eSAdrian Chadd * However, we need more frames right now because of 33449236b4eSAdrian Chadd * how the MAC implements the frame scheduling policy. 33549236b4eSAdrian Chadd * It only ungates a single FIFO entry at a time, 33649236b4eSAdrian Chadd * and will run that until CHNTIME expires or the 33749236b4eSAdrian Chadd * end of that FIFO entry descriptor list is reached. 33849236b4eSAdrian Chadd * So for TDMA we suffer a big performance penalty - 33949236b4eSAdrian Chadd * single TX FIFO entries mean the MAC only sends out 34049236b4eSAdrian Chadd * one frame per DBA event, which turned out on average 34149236b4eSAdrian Chadd * 6ms per TX frame. 34249236b4eSAdrian Chadd * 34349236b4eSAdrian Chadd * So, for aggregates it's okay - it'll push two at a 34449236b4eSAdrian Chadd * time and this will just do them more efficiently. 34549236b4eSAdrian Chadd * For non-aggregates it'll do 4 at a time, up to the 34649236b4eSAdrian Chadd * non-aggr limit (non_aggr, which is 32.) They should 34749236b4eSAdrian Chadd * be time based rather than a hard count, but I also 34849236b4eSAdrian Chadd * do need sleep. 34992e84e43SAdrian Chadd */ 3504f5ec72aSAdrian Chadd 3514f5ec72aSAdrian Chadd /* 3524f5ec72aSAdrian Chadd * Do some basic, basic batching to the hardware 3534f5ec72aSAdrian Chadd * queue. 3544f5ec72aSAdrian Chadd * 3554f5ec72aSAdrian Chadd * If we have TX_BATCH_SIZE entries in the staging 3564f5ec72aSAdrian Chadd * queue, then let's try to send them all in one hit. 3574f5ec72aSAdrian Chadd * 3584f5ec72aSAdrian Chadd * Ensure we don't push more than TX_BATCH_SIZE worth 3594f5ec72aSAdrian Chadd * in, otherwise we end up draining 8 slots worth of 3604f5ec72aSAdrian Chadd * 32 frames into the hardware queue and then we don't 3614f5ec72aSAdrian Chadd * attempt to push more frames in until we empty the 3624f5ec72aSAdrian Chadd * FIFO. 3634f5ec72aSAdrian Chadd */ 3644f5ec72aSAdrian Chadd if (txq->axq_depth >= TX_BATCH_SIZE / 2 && 3654f5ec72aSAdrian Chadd txq->fifo.axq_depth <= TX_BATCH_SIZE) { 3664f5ec72aSAdrian Chadd ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); 3674f5ec72aSAdrian Chadd } 3684f5ec72aSAdrian Chadd 3694f5ec72aSAdrian Chadd /* 3704f5ec72aSAdrian Chadd * Aggregate check: if we have less than two FIFO slots 3714f5ec72aSAdrian Chadd * busy and we have some aggregate frames, queue it. 3724f5ec72aSAdrian Chadd * 3734f5ec72aSAdrian Chadd * Now, ideally we'd just check to see if the scheduler 3744f5ec72aSAdrian Chadd * has given us aggregate frames and push them into the FIFO 3754f5ec72aSAdrian Chadd * as individual slots, as honestly we should just be pushing 3764f5ec72aSAdrian Chadd * a single aggregate in as one FIFO slot. 3774f5ec72aSAdrian Chadd * 3784f5ec72aSAdrian Chadd * Let's do that next once I know this works. 3794f5ec72aSAdrian Chadd */ 3804f5ec72aSAdrian Chadd else if (txq->axq_aggr_depth > 0 && txq->axq_fifo_depth < 2) 3814f5ec72aSAdrian Chadd ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); 3824f5ec72aSAdrian Chadd 3834f5ec72aSAdrian Chadd /* 3844f5ec72aSAdrian Chadd * 3854f5ec72aSAdrian Chadd * If we have less, and the TXFIFO isn't empty, let's 3864f5ec72aSAdrian Chadd * wait until we've finished sending the FIFO. 3874f5ec72aSAdrian Chadd * 3884f5ec72aSAdrian Chadd * If we have less, and the TXFIFO is empty, then 3894f5ec72aSAdrian Chadd * send them. 3904f5ec72aSAdrian Chadd */ 3914f5ec72aSAdrian Chadd else if (txq->axq_fifo_depth == 0) { 3924f5ec72aSAdrian Chadd ath_tx_edma_push_staging_list(sc, txq, TX_BATCH_SIZE); 3934f5ec72aSAdrian Chadd } 3944aa8818bSAdrian Chadd } 3954aa8818bSAdrian Chadd 396746bab5bSAdrian Chadd /* 397746bab5bSAdrian Chadd * Re-initialise the DMA FIFO with the current contents of 3983ae723d4SAdrian Chadd * said TXQ. 399746bab5bSAdrian Chadd * 400746bab5bSAdrian Chadd * This should only be called as part of the chip reset path, as it 401746bab5bSAdrian Chadd * assumes the FIFO is currently empty. 402746bab5bSAdrian Chadd */ 403746bab5bSAdrian Chadd static void 404746bab5bSAdrian Chadd ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq) 405746bab5bSAdrian Chadd { 40692e84e43SAdrian Chadd struct ath_buf *bf; 40792e84e43SAdrian Chadd int i = 0; 40892e84e43SAdrian Chadd int fifostart = 1; 40992e84e43SAdrian Chadd int old_fifo_depth; 410746bab5bSAdrian Chadd 41192e84e43SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: called\n", 412746bab5bSAdrian Chadd __func__, 413746bab5bSAdrian Chadd txq->axq_qnum); 4144aa8818bSAdrian Chadd 415b837332dSAdrian Chadd ATH_TXQ_LOCK_ASSERT(txq); 41692e84e43SAdrian Chadd 41792e84e43SAdrian Chadd /* 41892e84e43SAdrian Chadd * Let's log if the tracked FIFO depth doesn't match 41992e84e43SAdrian Chadd * what we actually push in. 42092e84e43SAdrian Chadd */ 42192e84e43SAdrian Chadd old_fifo_depth = txq->axq_fifo_depth; 42292e84e43SAdrian Chadd txq->axq_fifo_depth = 0; 42392e84e43SAdrian Chadd 42492e84e43SAdrian Chadd /* 42592e84e43SAdrian Chadd * Walk the FIFO staging list, looking for "head" entries. 42692e84e43SAdrian Chadd * Since we may have a partially completed list of frames, 42792e84e43SAdrian Chadd * we push the first frame we see into the FIFO and re-mark 42892e84e43SAdrian Chadd * it as the head entry. We then skip entries until we see 42992e84e43SAdrian Chadd * FIFO end, at which point we get ready to push another 43092e84e43SAdrian Chadd * entry into the FIFO. 43192e84e43SAdrian Chadd */ 43292e84e43SAdrian Chadd TAILQ_FOREACH(bf, &txq->fifo.axq_q, bf_list) { 43392e84e43SAdrian Chadd /* 43492e84e43SAdrian Chadd * If we're looking for FIFOEND and we haven't found 43592e84e43SAdrian Chadd * it, skip. 43692e84e43SAdrian Chadd * 43792e84e43SAdrian Chadd * If we're looking for FIFOEND and we've found it, 43892e84e43SAdrian Chadd * reset for another descriptor. 43992e84e43SAdrian Chadd */ 44092e84e43SAdrian Chadd #ifdef ATH_DEBUG 44192e84e43SAdrian Chadd if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) 44292e84e43SAdrian Chadd ath_printtxbuf(sc, bf, txq->axq_qnum, i, 0); 44392e84e43SAdrian Chadd #endif/* ATH_DEBUG */ 44492e84e43SAdrian Chadd #ifdef ATH_DEBUG_ALQ 44592e84e43SAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 44692e84e43SAdrian Chadd ath_tx_alq_post(sc, bf); 44792e84e43SAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 44892e84e43SAdrian Chadd 44992e84e43SAdrian Chadd if (fifostart == 0) { 45092e84e43SAdrian Chadd if (bf->bf_flags & ATH_BUF_FIFOEND) 45192e84e43SAdrian Chadd fifostart = 1; 45292e84e43SAdrian Chadd continue; 45392e84e43SAdrian Chadd } 45492e84e43SAdrian Chadd 45592e84e43SAdrian Chadd /* Make sure we're not overflowing the FIFO! */ 45692e84e43SAdrian Chadd if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) { 45792e84e43SAdrian Chadd device_printf(sc->sc_dev, 45892e84e43SAdrian Chadd "%s: Q%d: more frames in the queue; FIFO depth=%d?!\n", 45992e84e43SAdrian Chadd __func__, 46092e84e43SAdrian Chadd txq->axq_qnum, 46192e84e43SAdrian Chadd txq->axq_fifo_depth); 46292e84e43SAdrian Chadd } 46392e84e43SAdrian Chadd 46492e84e43SAdrian Chadd #if 0 46592e84e43SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, 46692e84e43SAdrian Chadd "%s: Q%d: depth=%d: pushing bf=%p; start=%d, end=%d\n", 46792e84e43SAdrian Chadd __func__, 46892e84e43SAdrian Chadd txq->axq_qnum, 46992e84e43SAdrian Chadd txq->axq_fifo_depth, 47092e84e43SAdrian Chadd bf, 47192e84e43SAdrian Chadd !! (bf->bf_flags & ATH_BUF_FIFOPTR), 47292e84e43SAdrian Chadd !! (bf->bf_flags & ATH_BUF_FIFOEND)); 47392e84e43SAdrian Chadd #endif 47492e84e43SAdrian Chadd 47592e84e43SAdrian Chadd /* 47692e84e43SAdrian Chadd * Set this to be the first buffer in the FIFO 47792e84e43SAdrian Chadd * list - even if it's also the last buffer in 47892e84e43SAdrian Chadd * a FIFO list! 47992e84e43SAdrian Chadd */ 48092e84e43SAdrian Chadd bf->bf_flags |= ATH_BUF_FIFOPTR; 48192e84e43SAdrian Chadd 48292e84e43SAdrian Chadd /* Push it into the FIFO and bump the FIFO count */ 48392e84e43SAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr); 48492e84e43SAdrian Chadd txq->axq_fifo_depth++; 48592e84e43SAdrian Chadd 48692e84e43SAdrian Chadd /* 48792e84e43SAdrian Chadd * If this isn't the last entry either, let's 48892e84e43SAdrian Chadd * clear fifostart so we continue looking for 48992e84e43SAdrian Chadd * said last entry. 49092e84e43SAdrian Chadd */ 49192e84e43SAdrian Chadd if (! (bf->bf_flags & ATH_BUF_FIFOEND)) 49292e84e43SAdrian Chadd fifostart = 0; 49392e84e43SAdrian Chadd i++; 49492e84e43SAdrian Chadd } 49592e84e43SAdrian Chadd 49692e84e43SAdrian Chadd /* Only bother starting the queue if there's something in it */ 49792e84e43SAdrian Chadd if (i > 0) 49892e84e43SAdrian Chadd ath_hal_txstart(sc->sc_ah, txq->axq_qnum); 49992e84e43SAdrian Chadd 50092e84e43SAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: FIFO depth was %d, is %d\n", 50192e84e43SAdrian Chadd __func__, 50292e84e43SAdrian Chadd txq->axq_qnum, 50392e84e43SAdrian Chadd old_fifo_depth, 50492e84e43SAdrian Chadd txq->axq_fifo_depth); 50592e84e43SAdrian Chadd 50692e84e43SAdrian Chadd /* And now, let's check! */ 50792e84e43SAdrian Chadd if (txq->axq_fifo_depth != old_fifo_depth) { 50892e84e43SAdrian Chadd device_printf(sc->sc_dev, 50992e84e43SAdrian Chadd "%s: Q%d: FIFO depth should be %d, is %d\n", 51092e84e43SAdrian Chadd __func__, 51192e84e43SAdrian Chadd txq->axq_qnum, 51292e84e43SAdrian Chadd old_fifo_depth, 51392e84e43SAdrian Chadd txq->axq_fifo_depth); 51492e84e43SAdrian Chadd } 515746bab5bSAdrian Chadd } 516746bab5bSAdrian Chadd 517746bab5bSAdrian Chadd /* 5183ae723d4SAdrian Chadd * Hand off this frame to a hardware queue. 5193ae723d4SAdrian Chadd * 5203ae723d4SAdrian Chadd * Things are a bit hairy in the EDMA world. The TX FIFO is only 5213ae723d4SAdrian Chadd * 8 entries deep, so we need to keep track of exactly what we've 5223ae723d4SAdrian Chadd * pushed into the FIFO and what's just sitting in the TX queue, 5233ae723d4SAdrian Chadd * waiting to go out. 5243ae723d4SAdrian Chadd * 5253ae723d4SAdrian Chadd * So this is split into two halves - frames get appended to the 5263ae723d4SAdrian Chadd * TXQ; then a scheduler is called to push some frames into the 5273ae723d4SAdrian Chadd * actual TX FIFO. 5283ae723d4SAdrian Chadd */ 5293ae723d4SAdrian Chadd static void 5303ae723d4SAdrian Chadd ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, 5313ae723d4SAdrian Chadd struct ath_buf *bf) 5323ae723d4SAdrian Chadd { 5333ae723d4SAdrian Chadd 5340acf45edSAdrian Chadd ATH_TXQ_LOCK(txq); 5353ae723d4SAdrian Chadd 5363ae723d4SAdrian Chadd KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, 5373ae723d4SAdrian Chadd ("%s: busy status 0x%x", __func__, bf->bf_flags)); 5383ae723d4SAdrian Chadd 5393ae723d4SAdrian Chadd /* 5403ae723d4SAdrian Chadd * XXX TODO: write a hard-coded check to ensure that 5413ae723d4SAdrian Chadd * the queue id in the TX descriptor matches txq->axq_qnum. 5423ae723d4SAdrian Chadd */ 5433ae723d4SAdrian Chadd 5443ae723d4SAdrian Chadd /* Update aggr stats */ 5453ae723d4SAdrian Chadd if (bf->bf_state.bfs_aggr) 5463ae723d4SAdrian Chadd txq->axq_aggr_depth++; 5473ae723d4SAdrian Chadd 5483ae723d4SAdrian Chadd /* Push and update frame stats */ 5493ae723d4SAdrian Chadd ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 5503ae723d4SAdrian Chadd 55192e84e43SAdrian Chadd /* 55292e84e43SAdrian Chadd * Finally, call the FIFO schedule routine to schedule some 55392e84e43SAdrian Chadd * frames to the FIFO. 55492e84e43SAdrian Chadd */ 55592e84e43SAdrian Chadd ath_edma_tx_fifo_fill(sc, txq); 5560acf45edSAdrian Chadd ATH_TXQ_UNLOCK(txq); 5573ae723d4SAdrian Chadd } 5583ae723d4SAdrian Chadd 5593ae723d4SAdrian Chadd /* 5603ae723d4SAdrian Chadd * Hand off this frame to a multicast software queue. 5613ae723d4SAdrian Chadd * 562e3f06688SAdrian Chadd * The EDMA TX CABQ will get a list of chained frames, chained 563e3f06688SAdrian Chadd * together using the next pointer. The single head of that 564e3f06688SAdrian Chadd * particular queue is pushed to the hardware CABQ. 5653ae723d4SAdrian Chadd */ 5663ae723d4SAdrian Chadd static void 5673ae723d4SAdrian Chadd ath_edma_xmit_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, 5683ae723d4SAdrian Chadd struct ath_buf *bf) 5693ae723d4SAdrian Chadd { 5703ae723d4SAdrian Chadd 5719e7259a2SAdrian Chadd ATH_TX_LOCK_ASSERT(sc); 5723ae723d4SAdrian Chadd KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, 5733ae723d4SAdrian Chadd ("%s: busy status 0x%x", __func__, bf->bf_flags)); 5743ae723d4SAdrian Chadd 5750acf45edSAdrian Chadd ATH_TXQ_LOCK(txq); 5763ae723d4SAdrian Chadd /* 5773ae723d4SAdrian Chadd * XXX this is mostly duplicated in ath_tx_handoff_mcast(). 5783ae723d4SAdrian Chadd */ 5799e7259a2SAdrian Chadd if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) { 5803ae723d4SAdrian Chadd struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s); 5813ae723d4SAdrian Chadd struct ieee80211_frame *wh; 5823ae723d4SAdrian Chadd 5833ae723d4SAdrian Chadd /* mark previous frame */ 5843ae723d4SAdrian Chadd wh = mtod(bf_last->bf_m, struct ieee80211_frame *); 5853ae723d4SAdrian Chadd wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 5863ae723d4SAdrian Chadd 587caedab2cSAdrian Chadd /* re-sync buffer to memory */ 5883ae723d4SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap, 5893ae723d4SAdrian Chadd BUS_DMASYNC_PREWRITE); 5909cda8c80SAdrian Chadd 5919cda8c80SAdrian Chadd /* link descriptor */ 5929e7259a2SAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, 5939e7259a2SAdrian Chadd bf_last->bf_lastds, 5949e7259a2SAdrian Chadd bf->bf_daddr); 5953ae723d4SAdrian Chadd } 596e3f06688SAdrian Chadd #ifdef ATH_DEBUG_ALQ 597e3f06688SAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC)) 598e3f06688SAdrian Chadd ath_tx_alq_post(sc, bf); 599e3f06688SAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 6003ae723d4SAdrian Chadd ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); 6010acf45edSAdrian Chadd ATH_TXQ_UNLOCK(txq); 6023ae723d4SAdrian Chadd } 6033ae723d4SAdrian Chadd 6043ae723d4SAdrian Chadd /* 605746bab5bSAdrian Chadd * Handoff this frame to the hardware. 606746bab5bSAdrian Chadd * 607746bab5bSAdrian Chadd * For the multicast queue, this will treat it as a software queue 608746bab5bSAdrian Chadd * and append it to the list, after updating the MORE_DATA flag 609746bab5bSAdrian Chadd * in the previous frame. The cabq processing code will ensure 610746bab5bSAdrian Chadd * that the queue contents gets transferred over. 611746bab5bSAdrian Chadd * 612746bab5bSAdrian Chadd * For the hardware queues, this will queue a frame to the queue 613746bab5bSAdrian Chadd * like before, then populate the FIFO from that. Since the 614746bab5bSAdrian Chadd * EDMA hardware has 8 FIFO slots per TXQ, this ensures that 615746bab5bSAdrian Chadd * frames such as management frames don't get prematurely dropped. 616746bab5bSAdrian Chadd * 617746bab5bSAdrian Chadd * This does imply that a similar flush-hwq-to-fifoq method will 618746bab5bSAdrian Chadd * need to be called from the processq function, before the 619746bab5bSAdrian Chadd * per-node software scheduler is called. 620746bab5bSAdrian Chadd */ 621746bab5bSAdrian Chadd static void 622746bab5bSAdrian Chadd ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, 623746bab5bSAdrian Chadd struct ath_buf *bf) 624746bab5bSAdrian Chadd { 625746bab5bSAdrian Chadd 6264aa8818bSAdrian Chadd DPRINTF(sc, ATH_DEBUG_XMIT_DESC, 6274aa8818bSAdrian Chadd "%s: called; bf=%p, txq=%p, qnum=%d\n", 628746bab5bSAdrian Chadd __func__, 629746bab5bSAdrian Chadd bf, 630746bab5bSAdrian Chadd txq, 631746bab5bSAdrian Chadd txq->axq_qnum); 632746bab5bSAdrian Chadd 6333ae723d4SAdrian Chadd if (txq->axq_qnum == ATH_TXQ_SWQ) 6343ae723d4SAdrian Chadd ath_edma_xmit_handoff_mcast(sc, txq, bf); 6353ae723d4SAdrian Chadd else 6363ae723d4SAdrian Chadd ath_edma_xmit_handoff_hw(sc, txq, bf); 637746bab5bSAdrian Chadd } 638746bab5bSAdrian Chadd 6393fdfc330SAdrian Chadd static int 64079607afeSAdrian Chadd ath_edma_setup_txfifo(struct ath_softc *sc, int qnum) 64179607afeSAdrian Chadd { 64279607afeSAdrian Chadd struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; 64379607afeSAdrian Chadd 64479607afeSAdrian Chadd te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH, 64579607afeSAdrian Chadd M_ATHDEV, 64679607afeSAdrian Chadd M_NOWAIT | M_ZERO); 64779607afeSAdrian Chadd if (te->m_fifo == NULL) { 64879607afeSAdrian Chadd device_printf(sc->sc_dev, "%s: malloc failed\n", 64979607afeSAdrian Chadd __func__); 65079607afeSAdrian Chadd return (-ENOMEM); 65179607afeSAdrian Chadd } 65279607afeSAdrian Chadd 65379607afeSAdrian Chadd /* 65479607afeSAdrian Chadd * Set initial "empty" state. 65579607afeSAdrian Chadd */ 65679607afeSAdrian Chadd te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0; 65779607afeSAdrian Chadd 65879607afeSAdrian Chadd return (0); 65979607afeSAdrian Chadd } 66079607afeSAdrian Chadd 66179607afeSAdrian Chadd static int 66279607afeSAdrian Chadd ath_edma_free_txfifo(struct ath_softc *sc, int qnum) 66379607afeSAdrian Chadd { 66479607afeSAdrian Chadd struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; 66579607afeSAdrian Chadd 66679607afeSAdrian Chadd /* XXX TODO: actually deref the ath_buf entries? */ 66779607afeSAdrian Chadd free(te->m_fifo, M_ATHDEV); 66879607afeSAdrian Chadd return (0); 66979607afeSAdrian Chadd } 67079607afeSAdrian Chadd 67179607afeSAdrian Chadd static int 6723fdfc330SAdrian Chadd ath_edma_dma_txsetup(struct ath_softc *sc) 6733fdfc330SAdrian Chadd { 674ba3fd9d8SAdrian Chadd int error; 67579607afeSAdrian Chadd int i; 6763fdfc330SAdrian Chadd 677ba3fd9d8SAdrian Chadd error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma, 678ba3fd9d8SAdrian Chadd NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE); 679ba3fd9d8SAdrian Chadd if (error != 0) 680ba3fd9d8SAdrian Chadd return (error); 681ba3fd9d8SAdrian Chadd 682ba3fd9d8SAdrian Chadd ath_hal_setuptxstatusring(sc->sc_ah, 683ba3fd9d8SAdrian Chadd (void *) sc->sc_txsdma.dd_desc, 684ba3fd9d8SAdrian Chadd sc->sc_txsdma.dd_desc_paddr, 685ba3fd9d8SAdrian Chadd ATH_TXSTATUS_RING_SIZE); 686ba3fd9d8SAdrian Chadd 68779607afeSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 68879607afeSAdrian Chadd ath_edma_setup_txfifo(sc, i); 68979607afeSAdrian Chadd } 69079607afeSAdrian Chadd 6913fdfc330SAdrian Chadd return (0); 6923fdfc330SAdrian Chadd } 6933fdfc330SAdrian Chadd 6943fdfc330SAdrian Chadd static int 6953fdfc330SAdrian Chadd ath_edma_dma_txteardown(struct ath_softc *sc) 6963fdfc330SAdrian Chadd { 69779607afeSAdrian Chadd int i; 69879607afeSAdrian Chadd 69979607afeSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 70079607afeSAdrian Chadd ath_edma_free_txfifo(sc, i); 70179607afeSAdrian Chadd } 7023fdfc330SAdrian Chadd 703ba3fd9d8SAdrian Chadd ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL); 7043fdfc330SAdrian Chadd return (0); 7053fdfc330SAdrian Chadd } 7063fdfc330SAdrian Chadd 7073ae723d4SAdrian Chadd /* 708788e6aa9SAdrian Chadd * Drain all TXQs, potentially after completing the existing completed 709788e6aa9SAdrian Chadd * frames. 7103ae723d4SAdrian Chadd */ 711788e6aa9SAdrian Chadd static void 712788e6aa9SAdrian Chadd ath_edma_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type) 713f8418db5SAdrian Chadd { 7144aa8818bSAdrian Chadd int i; 715f8418db5SAdrian Chadd 716ae3815fdSAdrian Chadd DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); 7174aa8818bSAdrian Chadd 7184aa8818bSAdrian Chadd (void) ath_stoptxdma(sc); 7194aa8818bSAdrian Chadd 7204aa8818bSAdrian Chadd /* 7214aa8818bSAdrian Chadd * If reset type is noloss, the TX FIFO needs to be serviced 7224aa8818bSAdrian Chadd * and those frames need to be handled. 7234aa8818bSAdrian Chadd * 7244aa8818bSAdrian Chadd * Otherwise, just toss everything in each TX queue. 7254aa8818bSAdrian Chadd */ 726ae3815fdSAdrian Chadd if (reset_type == ATH_RESET_NOLOSS) { 727ae3815fdSAdrian Chadd ath_edma_tx_processq(sc, 0); 7288328d6e4SAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 7298328d6e4SAdrian Chadd if (ATH_TXQ_SETUP(sc, i)) { 7308328d6e4SAdrian Chadd ATH_TXQ_LOCK(&sc->sc_txq[i]); 7318328d6e4SAdrian Chadd /* 7328328d6e4SAdrian Chadd * Free the holding buffer; DMA is now 7338328d6e4SAdrian Chadd * stopped. 7348328d6e4SAdrian Chadd */ 7358328d6e4SAdrian Chadd ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]); 7368328d6e4SAdrian Chadd /* 7378328d6e4SAdrian Chadd * Reset the link pointer to NULL; there's 7388328d6e4SAdrian Chadd * no frames to chain DMA to. 7398328d6e4SAdrian Chadd */ 7408328d6e4SAdrian Chadd sc->sc_txq[i].axq_link = NULL; 7418328d6e4SAdrian Chadd ATH_TXQ_UNLOCK(&sc->sc_txq[i]); 7428328d6e4SAdrian Chadd } 7438328d6e4SAdrian Chadd } 744ae3815fdSAdrian Chadd } else { 7454aa8818bSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 7464aa8818bSAdrian Chadd if (ATH_TXQ_SETUP(sc, i)) 7474aa8818bSAdrian Chadd ath_tx_draintxq(sc, &sc->sc_txq[i]); 7484aa8818bSAdrian Chadd } 749ae3815fdSAdrian Chadd } 750ae3815fdSAdrian Chadd 751ae3815fdSAdrian Chadd /* XXX dump out the TX completion FIFO contents */ 752ae3815fdSAdrian Chadd 753ae3815fdSAdrian Chadd /* XXX dump out the frames */ 7544aa8818bSAdrian Chadd 7554aa8818bSAdrian Chadd sc->sc_wd_timer = 0; 756f8418db5SAdrian Chadd } 757f8418db5SAdrian Chadd 7583ae723d4SAdrian Chadd /* 759ae3815fdSAdrian Chadd * TX completion tasklet. 7603ae723d4SAdrian Chadd */ 761ae3815fdSAdrian Chadd 762f8418db5SAdrian Chadd static void 763f8418db5SAdrian Chadd ath_edma_tx_proc(void *arg, int npending) 764f8418db5SAdrian Chadd { 765f8418db5SAdrian Chadd struct ath_softc *sc = (struct ath_softc *) arg; 766ae3815fdSAdrian Chadd 767908341abSAdrian Chadd ATH_PCU_LOCK(sc); 768908341abSAdrian Chadd sc->sc_txproc_cnt++; 769908341abSAdrian Chadd ATH_PCU_UNLOCK(sc); 770908341abSAdrian Chadd 771908341abSAdrian Chadd ATH_LOCK(sc); 772908341abSAdrian Chadd ath_power_set_power_state(sc, HAL_PM_AWAKE); 773908341abSAdrian Chadd ATH_UNLOCK(sc); 774908341abSAdrian Chadd 77592e84e43SAdrian Chadd #if 0 776ae3815fdSAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called, npending=%d\n", 777ae3815fdSAdrian Chadd __func__, npending); 77892e84e43SAdrian Chadd #endif 779ae3815fdSAdrian Chadd ath_edma_tx_processq(sc, 1); 780908341abSAdrian Chadd 781908341abSAdrian Chadd 782908341abSAdrian Chadd ATH_PCU_LOCK(sc); 783908341abSAdrian Chadd sc->sc_txproc_cnt--; 784908341abSAdrian Chadd ATH_PCU_UNLOCK(sc); 785908341abSAdrian Chadd 786908341abSAdrian Chadd ATH_LOCK(sc); 787908341abSAdrian Chadd ath_power_restore_power_state(sc); 788908341abSAdrian Chadd ATH_UNLOCK(sc); 789908341abSAdrian Chadd 790908341abSAdrian Chadd ath_tx_kick(sc); 791ae3815fdSAdrian Chadd } 792ae3815fdSAdrian Chadd 793ae3815fdSAdrian Chadd /* 794ae3815fdSAdrian Chadd * Process the TX status queue. 795ae3815fdSAdrian Chadd */ 796ae3815fdSAdrian Chadd static void 797ae3815fdSAdrian Chadd ath_edma_tx_processq(struct ath_softc *sc, int dosched) 798ae3815fdSAdrian Chadd { 7993ae723d4SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 8003ae723d4SAdrian Chadd HAL_STATUS status; 8013ae723d4SAdrian Chadd struct ath_tx_status ts; 8023ae723d4SAdrian Chadd struct ath_txq *txq; 8034aa8818bSAdrian Chadd struct ath_buf *bf; 8044aa8818bSAdrian Chadd struct ieee80211_node *ni; 805208be709SAdrian Chadd int nacked = 0; 806d40c846aSAdrian Chadd int idx; 8074f5ec72aSAdrian Chadd int i; 808d40c846aSAdrian Chadd 809d40c846aSAdrian Chadd #ifdef ATH_DEBUG 810d40c846aSAdrian Chadd /* XXX */ 811d40c846aSAdrian Chadd uint32_t txstatus[32]; 812d40c846aSAdrian Chadd #endif 813f8418db5SAdrian Chadd 8146eb9f206SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called\n", __func__); 8156eb9f206SAdrian Chadd 816d40c846aSAdrian Chadd for (idx = 0; ; idx++) { 8174aa8818bSAdrian Chadd bzero(&ts, sizeof(ts)); 8184aa8818bSAdrian Chadd 8193ae723d4SAdrian Chadd ATH_TXSTATUS_LOCK(sc); 8204c5038c7SAdrian Chadd #ifdef ATH_DEBUG 821d40c846aSAdrian Chadd ath_hal_gettxrawtxdesc(ah, txstatus); 8224c5038c7SAdrian Chadd #endif 823ae3815fdSAdrian Chadd status = ath_hal_txprocdesc(ah, NULL, (void *) &ts); 8243ae723d4SAdrian Chadd ATH_TXSTATUS_UNLOCK(sc); 8253ae723d4SAdrian Chadd 8266eb9f206SAdrian Chadd if (status == HAL_EINPROGRESS) { 8276eb9f206SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, 8286eb9f206SAdrian Chadd "%s: (%d): EINPROGRESS\n", 8296eb9f206SAdrian Chadd __func__, idx); 83092e84e43SAdrian Chadd break; 8316eb9f206SAdrian Chadd } 83292e84e43SAdrian Chadd 833d40c846aSAdrian Chadd #ifdef ATH_DEBUG 834d40c846aSAdrian Chadd if (sc->sc_debug & ATH_DEBUG_TX_PROC) 83592e84e43SAdrian Chadd if (ts.ts_queue_id != sc->sc_bhalq) 836d40c846aSAdrian Chadd ath_printtxstatbuf(sc, NULL, txstatus, ts.ts_queue_id, 837d40c846aSAdrian Chadd idx, (status == HAL_OK)); 838d40c846aSAdrian Chadd #endif 839d40c846aSAdrian Chadd 8403ae723d4SAdrian Chadd /* 8414aa8818bSAdrian Chadd * If there is an error with this descriptor, continue 8424aa8818bSAdrian Chadd * processing. 8434aa8818bSAdrian Chadd * 8444aa8818bSAdrian Chadd * XXX TBD: log some statistics? 8454aa8818bSAdrian Chadd */ 8464aa8818bSAdrian Chadd if (status == HAL_EIO) { 8474aa8818bSAdrian Chadd device_printf(sc->sc_dev, "%s: invalid TX status?\n", 8484aa8818bSAdrian Chadd __func__); 849b92b5f6eSAdrian Chadd break; 8504aa8818bSAdrian Chadd } 8514aa8818bSAdrian Chadd 85269cbcb21SAdrian Chadd #if defined(ATH_DEBUG_ALQ) && defined(ATH_DEBUG) 853bd3b3362SAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS)) { 854b69b0dccSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS, 855b69b0dccSAdrian Chadd sc->sc_tx_statuslen, 856b69b0dccSAdrian Chadd (char *) txstatus); 857bd3b3362SAdrian Chadd } 858b69b0dccSAdrian Chadd #endif /* ATH_DEBUG_ALQ */ 859b69b0dccSAdrian Chadd 8604aa8818bSAdrian Chadd /* 8613ae723d4SAdrian Chadd * At this point we have a valid status descriptor. 8623ae723d4SAdrian Chadd * The QID and descriptor ID (which currently isn't set) 8633ae723d4SAdrian Chadd * is part of the status. 8643ae723d4SAdrian Chadd * 8653ae723d4SAdrian Chadd * We then assume that the descriptor in question is the 8663ae723d4SAdrian Chadd * -head- of the given QID. Eventually we should verify 8673ae723d4SAdrian Chadd * this by using the descriptor ID. 8683ae723d4SAdrian Chadd */ 8694aa8818bSAdrian Chadd 8704aa8818bSAdrian Chadd /* 8714aa8818bSAdrian Chadd * The beacon queue is not currently a "real" queue. 8724aa8818bSAdrian Chadd * Frames aren't pushed onto it and the lock isn't setup. 8734aa8818bSAdrian Chadd * So skip it for now; the beacon handling code will 8744aa8818bSAdrian Chadd * free and alloc more beacon buffers as appropriate. 8754aa8818bSAdrian Chadd */ 8764aa8818bSAdrian Chadd if (ts.ts_queue_id == sc->sc_bhalq) 8774aa8818bSAdrian Chadd continue; 8783ae723d4SAdrian Chadd 8793ae723d4SAdrian Chadd txq = &sc->sc_txq[ts.ts_queue_id]; 8804aa8818bSAdrian Chadd 881b837332dSAdrian Chadd ATH_TXQ_LOCK(txq); 88292e84e43SAdrian Chadd bf = ATH_TXQ_FIRST(&txq->fifo); 8834aa8818bSAdrian Chadd 88492e84e43SAdrian Chadd /* 88592e84e43SAdrian Chadd * Work around the situation where I'm seeing notifications 88692e84e43SAdrian Chadd * for Q1 when no frames are available. That needs to be 88792e84e43SAdrian Chadd * debugged but not by crashing _here_. 88892e84e43SAdrian Chadd */ 88992e84e43SAdrian Chadd if (bf == NULL) { 89092e84e43SAdrian Chadd device_printf(sc->sc_dev, "%s: Q%d: empty?\n", 8914aa8818bSAdrian Chadd __func__, 89292e84e43SAdrian Chadd ts.ts_queue_id); 893b92b5f6eSAdrian Chadd ATH_TXQ_UNLOCK(txq); 89492e84e43SAdrian Chadd continue; 89592e84e43SAdrian Chadd } 89692e84e43SAdrian Chadd 89792e84e43SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d, bf=%p, start=%d, end=%d\n", 89892e84e43SAdrian Chadd __func__, 89992e84e43SAdrian Chadd ts.ts_queue_id, bf, 90092e84e43SAdrian Chadd !! (bf->bf_flags & ATH_BUF_FIFOPTR), 90192e84e43SAdrian Chadd !! (bf->bf_flags & ATH_BUF_FIFOEND)); 9024aa8818bSAdrian Chadd 903d40c846aSAdrian Chadd /* XXX TODO: actually output debugging info about this */ 904d40c846aSAdrian Chadd 9054aa8818bSAdrian Chadd #if 0 9064aa8818bSAdrian Chadd /* XXX assert the buffer/descriptor matches the status descid */ 9074aa8818bSAdrian Chadd if (ts.ts_desc_id != bf->bf_descid) { 9084aa8818bSAdrian Chadd device_printf(sc->sc_dev, 9094aa8818bSAdrian Chadd "%s: mismatched descid (qid=%d, tsdescid=%d, " 9104aa8818bSAdrian Chadd "bfdescid=%d\n", 9114aa8818bSAdrian Chadd __func__, 9124aa8818bSAdrian Chadd ts.ts_queue_id, 9134aa8818bSAdrian Chadd ts.ts_desc_id, 9144aa8818bSAdrian Chadd bf->bf_descid); 9153ae723d4SAdrian Chadd } 9164aa8818bSAdrian Chadd #endif 9174aa8818bSAdrian Chadd 9184aa8818bSAdrian Chadd /* This removes the buffer and decrements the queue depth */ 91992e84e43SAdrian Chadd ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list); 9204aa8818bSAdrian Chadd if (bf->bf_state.bfs_aggr) 9214aa8818bSAdrian Chadd txq->axq_aggr_depth--; 92292e84e43SAdrian Chadd 92392e84e43SAdrian Chadd /* 92492e84e43SAdrian Chadd * If this was the end of a FIFO set, decrement FIFO depth 92592e84e43SAdrian Chadd */ 92692e84e43SAdrian Chadd if (bf->bf_flags & ATH_BUF_FIFOEND) 9274aa8818bSAdrian Chadd txq->axq_fifo_depth--; 92892e84e43SAdrian Chadd 92992e84e43SAdrian Chadd /* 93092e84e43SAdrian Chadd * If this isn't the final buffer in a FIFO set, mark 93192e84e43SAdrian Chadd * the buffer as busy so it goes onto the holding queue. 93292e84e43SAdrian Chadd */ 93392e84e43SAdrian Chadd if (! (bf->bf_flags & ATH_BUF_FIFOEND)) 93492e84e43SAdrian Chadd bf->bf_flags |= ATH_BUF_BUSY; 93592e84e43SAdrian Chadd 93692e84e43SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d: FIFO depth is now %d (%d)\n", 93792e84e43SAdrian Chadd __func__, 93892e84e43SAdrian Chadd txq->axq_qnum, 93992e84e43SAdrian Chadd txq->axq_fifo_depth, 94092e84e43SAdrian Chadd txq->fifo.axq_depth); 94192e84e43SAdrian Chadd 9424aa8818bSAdrian Chadd /* XXX assert FIFO depth >= 0 */ 943b837332dSAdrian Chadd ATH_TXQ_UNLOCK(txq); 9444aa8818bSAdrian Chadd 9454aa8818bSAdrian Chadd /* 94692e84e43SAdrian Chadd * Outside of the TX lock - if the buffer is end 94792e84e43SAdrian Chadd * end buffer in this FIFO, we don't need a holding 94892e84e43SAdrian Chadd * buffer any longer. 94992e84e43SAdrian Chadd */ 95092e84e43SAdrian Chadd if (bf->bf_flags & ATH_BUF_FIFOEND) { 951caedab2cSAdrian Chadd ATH_TXQ_LOCK(txq); 95292e84e43SAdrian Chadd ath_txq_freeholdingbuf(sc, txq); 953caedab2cSAdrian Chadd ATH_TXQ_UNLOCK(txq); 95492e84e43SAdrian Chadd } 95592e84e43SAdrian Chadd 95692e84e43SAdrian Chadd /* 9574aa8818bSAdrian Chadd * First we need to make sure ts_rate is valid. 9584aa8818bSAdrian Chadd * 9594aa8818bSAdrian Chadd * Pre-EDMA chips pass the whole TX descriptor to 9604aa8818bSAdrian Chadd * the proctxdesc function which will then fill out 9614aa8818bSAdrian Chadd * ts_rate based on the ts_finaltsi (final TX index) 9624aa8818bSAdrian Chadd * in the TX descriptor. However the TX completion 9634aa8818bSAdrian Chadd * FIFO doesn't have this information. So here we 9644aa8818bSAdrian Chadd * do a separate HAL call to populate that information. 9653345c65bSAdrian Chadd * 9663345c65bSAdrian Chadd * The same problem exists with ts_longretry. 9673345c65bSAdrian Chadd * The FreeBSD HAL corrects ts_longretry in the HAL layer; 9683345c65bSAdrian Chadd * the AR9380 HAL currently doesn't. So until the HAL 9693345c65bSAdrian Chadd * is imported and this can be added, we correct for it 9703345c65bSAdrian Chadd * here. 9714aa8818bSAdrian Chadd */ 9724aa8818bSAdrian Chadd /* XXX TODO */ 9734aa8818bSAdrian Chadd /* XXX faked for now. Ew. */ 9744aa8818bSAdrian Chadd if (ts.ts_finaltsi < 4) { 9754aa8818bSAdrian Chadd ts.ts_rate = 9764aa8818bSAdrian Chadd bf->bf_state.bfs_rc[ts.ts_finaltsi].ratecode; 9773345c65bSAdrian Chadd switch (ts.ts_finaltsi) { 9783345c65bSAdrian Chadd case 3: ts.ts_longretry += 9793345c65bSAdrian Chadd bf->bf_state.bfs_rc[2].tries; 9803345c65bSAdrian Chadd case 2: ts.ts_longretry += 9813345c65bSAdrian Chadd bf->bf_state.bfs_rc[1].tries; 9823345c65bSAdrian Chadd case 1: ts.ts_longretry += 9833345c65bSAdrian Chadd bf->bf_state.bfs_rc[0].tries; 9843345c65bSAdrian Chadd } 9854aa8818bSAdrian Chadd } else { 9864aa8818bSAdrian Chadd device_printf(sc->sc_dev, "%s: finaltsi=%d\n", 9874aa8818bSAdrian Chadd __func__, 9884aa8818bSAdrian Chadd ts.ts_finaltsi); 9894aa8818bSAdrian Chadd ts.ts_rate = bf->bf_state.bfs_rc[0].ratecode; 9904aa8818bSAdrian Chadd } 9914aa8818bSAdrian Chadd 9924aa8818bSAdrian Chadd /* 9934aa8818bSAdrian Chadd * XXX This is terrible. 9944aa8818bSAdrian Chadd * 9954aa8818bSAdrian Chadd * Right now, some code uses the TX status that is 9964aa8818bSAdrian Chadd * passed in here, but the completion handlers in the 9974aa8818bSAdrian Chadd * software TX path also use bf_status.ds_txstat. 9984aa8818bSAdrian Chadd * Ew. That should all go away. 9994aa8818bSAdrian Chadd * 10004aa8818bSAdrian Chadd * XXX It's also possible the rate control completion 10014aa8818bSAdrian Chadd * routine is called twice. 10024aa8818bSAdrian Chadd */ 10034aa8818bSAdrian Chadd memcpy(&bf->bf_status, &ts, sizeof(ts)); 10044aa8818bSAdrian Chadd 10054aa8818bSAdrian Chadd ni = bf->bf_node; 10064aa8818bSAdrian Chadd 10074aa8818bSAdrian Chadd /* Update RSSI */ 10084aa8818bSAdrian Chadd /* XXX duplicate from ath_tx_processq */ 10094aa8818bSAdrian Chadd if (ni != NULL && ts.ts_status == 0 && 10104aa8818bSAdrian Chadd ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) { 10114aa8818bSAdrian Chadd nacked++; 10124aa8818bSAdrian Chadd sc->sc_stats.ast_tx_rssi = ts.ts_rssi; 10134aa8818bSAdrian Chadd ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, 10144aa8818bSAdrian Chadd ts.ts_rssi); 10154aa8818bSAdrian Chadd } 10164aa8818bSAdrian Chadd 10174aa8818bSAdrian Chadd /* Handle frame completion and rate control update */ 10184aa8818bSAdrian Chadd ath_tx_process_buf_completion(sc, txq, &ts, bf); 10194aa8818bSAdrian Chadd 10204f5ec72aSAdrian Chadd /* NB: bf is invalid at this point */ 10214aa8818bSAdrian Chadd } 10224aa8818bSAdrian Chadd 10234aa8818bSAdrian Chadd sc->sc_wd_timer = 0; 10244aa8818bSAdrian Chadd 10254aa8818bSAdrian Chadd /* 10264aa8818bSAdrian Chadd * XXX It's inefficient to do this if the FIFO queue is full, 10274aa8818bSAdrian Chadd * but there's no easy way right now to only populate 10284aa8818bSAdrian Chadd * the txq task for _one_ TXQ. This should be fixed. 10294aa8818bSAdrian Chadd */ 10304f5ec72aSAdrian Chadd if (dosched) { 10314f5ec72aSAdrian Chadd /* Attempt to schedule more hardware frames to the TX FIFO */ 10324f5ec72aSAdrian Chadd for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 10334f5ec72aSAdrian Chadd if (ATH_TXQ_SETUP(sc, i)) { 10346eb9f206SAdrian Chadd ATH_TX_LOCK(sc); 10356eb9f206SAdrian Chadd ath_txq_sched(sc, &sc->sc_txq[i]); 10366eb9f206SAdrian Chadd ATH_TX_UNLOCK(sc); 10376eb9f206SAdrian Chadd 10384f5ec72aSAdrian Chadd ATH_TXQ_LOCK(&sc->sc_txq[i]); 10394f5ec72aSAdrian Chadd ath_edma_tx_fifo_fill(sc, &sc->sc_txq[i]); 10404f5ec72aSAdrian Chadd ATH_TXQ_UNLOCK(&sc->sc_txq[i]); 10414f5ec72aSAdrian Chadd } 10424f5ec72aSAdrian Chadd } 10434f5ec72aSAdrian Chadd /* Kick software scheduler */ 104421bca442SAdrian Chadd ath_tx_swq_kick(sc); 1045f8418db5SAdrian Chadd } 10466eb9f206SAdrian Chadd 10476eb9f206SAdrian Chadd DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: end\n", __func__); 10484f5ec72aSAdrian Chadd } 1049f8418db5SAdrian Chadd 1050f8418db5SAdrian Chadd static void 1051f8418db5SAdrian Chadd ath_edma_attach_comp_func(struct ath_softc *sc) 1052f8418db5SAdrian Chadd { 1053f8418db5SAdrian Chadd 1054f8418db5SAdrian Chadd TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc); 1055f8418db5SAdrian Chadd } 1056f8418db5SAdrian Chadd 10573fdfc330SAdrian Chadd void 10583fdfc330SAdrian Chadd ath_xmit_setup_edma(struct ath_softc *sc) 10593fdfc330SAdrian Chadd { 10603fdfc330SAdrian Chadd 10613fdfc330SAdrian Chadd /* Fetch EDMA field and buffer sizes */ 10623fdfc330SAdrian Chadd (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); 10633fdfc330SAdrian Chadd (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); 10643fdfc330SAdrian Chadd (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); 10653fdfc330SAdrian Chadd 1066516a0ac2SAdrian Chadd if (bootverbose) { 10673fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX descriptor length: %d\n", 10683fdfc330SAdrian Chadd sc->sc_tx_desclen); 10693fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX status length: %d\n", 10703fdfc330SAdrian Chadd sc->sc_tx_statuslen); 10713fdfc330SAdrian Chadd device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", 10723fdfc330SAdrian Chadd sc->sc_tx_nmaps); 1073516a0ac2SAdrian Chadd } 10743fdfc330SAdrian Chadd 10753fdfc330SAdrian Chadd sc->sc_tx.xmit_setup = ath_edma_dma_txsetup; 10763fdfc330SAdrian Chadd sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown; 1077f8418db5SAdrian Chadd sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func; 1078746bab5bSAdrian Chadd 1079746bab5bSAdrian Chadd sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart; 1080746bab5bSAdrian Chadd sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff; 1081788e6aa9SAdrian Chadd sc->sc_tx.xmit_drain = ath_edma_tx_drain; 10823fdfc330SAdrian Chadd } 1083