xref: /freebsd/sys/dev/ath/if_ath_tx_edma.c (revision 3ae723d459e23ec955519705387bcefbc1180157)
13fdfc330SAdrian Chadd /*-
23fdfc330SAdrian Chadd  * Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org>
33fdfc330SAdrian Chadd  * All rights reserved.
43fdfc330SAdrian Chadd  *
53fdfc330SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
63fdfc330SAdrian Chadd  * modification, are permitted provided that the following conditions
73fdfc330SAdrian Chadd  * are met:
83fdfc330SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
93fdfc330SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
103fdfc330SAdrian Chadd  *    without modification.
113fdfc330SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
123fdfc330SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
133fdfc330SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
143fdfc330SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
153fdfc330SAdrian Chadd  *
163fdfc330SAdrian Chadd  * NO WARRANTY
173fdfc330SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
183fdfc330SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
193fdfc330SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
203fdfc330SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
213fdfc330SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
223fdfc330SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
233fdfc330SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
243fdfc330SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
253fdfc330SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
263fdfc330SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
273fdfc330SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
283fdfc330SAdrian Chadd  */
293fdfc330SAdrian Chadd 
303fdfc330SAdrian Chadd #include <sys/cdefs.h>
313fdfc330SAdrian Chadd __FBSDID("$FreeBSD$");
323fdfc330SAdrian Chadd 
333fdfc330SAdrian Chadd /*
343fdfc330SAdrian Chadd  * Driver for the Atheros Wireless LAN controller.
353fdfc330SAdrian Chadd  *
363fdfc330SAdrian Chadd  * This software is derived from work of Atsushi Onoe; his contribution
373fdfc330SAdrian Chadd  * is greatly appreciated.
383fdfc330SAdrian Chadd  */
393fdfc330SAdrian Chadd 
403fdfc330SAdrian Chadd #include "opt_inet.h"
413fdfc330SAdrian Chadd #include "opt_ath.h"
423fdfc330SAdrian Chadd /*
433fdfc330SAdrian Chadd  * This is needed for register operations which are performed
443fdfc330SAdrian Chadd  * by the driver - eg, calls to ath_hal_gettsf32().
453fdfc330SAdrian Chadd  *
463fdfc330SAdrian Chadd  * It's also required for any AH_DEBUG checks in here, eg the
473fdfc330SAdrian Chadd  * module dependencies.
483fdfc330SAdrian Chadd  */
493fdfc330SAdrian Chadd #include "opt_ah.h"
503fdfc330SAdrian Chadd #include "opt_wlan.h"
513fdfc330SAdrian Chadd 
523fdfc330SAdrian Chadd #include <sys/param.h>
533fdfc330SAdrian Chadd #include <sys/systm.h>
543fdfc330SAdrian Chadd #include <sys/sysctl.h>
553fdfc330SAdrian Chadd #include <sys/mbuf.h>
563fdfc330SAdrian Chadd #include <sys/malloc.h>
573fdfc330SAdrian Chadd #include <sys/lock.h>
583fdfc330SAdrian Chadd #include <sys/mutex.h>
593fdfc330SAdrian Chadd #include <sys/kernel.h>
603fdfc330SAdrian Chadd #include <sys/socket.h>
613fdfc330SAdrian Chadd #include <sys/sockio.h>
623fdfc330SAdrian Chadd #include <sys/errno.h>
633fdfc330SAdrian Chadd #include <sys/callout.h>
643fdfc330SAdrian Chadd #include <sys/bus.h>
653fdfc330SAdrian Chadd #include <sys/endian.h>
663fdfc330SAdrian Chadd #include <sys/kthread.h>
673fdfc330SAdrian Chadd #include <sys/taskqueue.h>
683fdfc330SAdrian Chadd #include <sys/priv.h>
693fdfc330SAdrian Chadd #include <sys/module.h>
703fdfc330SAdrian Chadd #include <sys/ktr.h>
713fdfc330SAdrian Chadd #include <sys/smp.h>	/* for mp_ncpus */
723fdfc330SAdrian Chadd 
733fdfc330SAdrian Chadd #include <machine/bus.h>
743fdfc330SAdrian Chadd 
753fdfc330SAdrian Chadd #include <net/if.h>
763fdfc330SAdrian Chadd #include <net/if_dl.h>
773fdfc330SAdrian Chadd #include <net/if_media.h>
783fdfc330SAdrian Chadd #include <net/if_types.h>
793fdfc330SAdrian Chadd #include <net/if_arp.h>
803fdfc330SAdrian Chadd #include <net/ethernet.h>
813fdfc330SAdrian Chadd #include <net/if_llc.h>
823fdfc330SAdrian Chadd 
833fdfc330SAdrian Chadd #include <net80211/ieee80211_var.h>
843fdfc330SAdrian Chadd #include <net80211/ieee80211_regdomain.h>
853fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG
863fdfc330SAdrian Chadd #include <net80211/ieee80211_superg.h>
873fdfc330SAdrian Chadd #endif
883fdfc330SAdrian Chadd #ifdef IEEE80211_SUPPORT_TDMA
893fdfc330SAdrian Chadd #include <net80211/ieee80211_tdma.h>
903fdfc330SAdrian Chadd #endif
913fdfc330SAdrian Chadd 
923fdfc330SAdrian Chadd #include <net/bpf.h>
933fdfc330SAdrian Chadd 
943fdfc330SAdrian Chadd #ifdef INET
953fdfc330SAdrian Chadd #include <netinet/in.h>
963fdfc330SAdrian Chadd #include <netinet/if_ether.h>
973fdfc330SAdrian Chadd #endif
983fdfc330SAdrian Chadd 
993fdfc330SAdrian Chadd #include <dev/ath/if_athvar.h>
1003fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_devid.h>		/* XXX for softled */
1013fdfc330SAdrian Chadd #include <dev/ath/ath_hal/ah_diagcodes.h>
1023fdfc330SAdrian Chadd 
1033fdfc330SAdrian Chadd #include <dev/ath/if_ath_debug.h>
1043fdfc330SAdrian Chadd #include <dev/ath/if_ath_misc.h>
1053fdfc330SAdrian Chadd #include <dev/ath/if_ath_tsf.h>
1063fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx.h>
1073fdfc330SAdrian Chadd #include <dev/ath/if_ath_sysctl.h>
1083fdfc330SAdrian Chadd #include <dev/ath/if_ath_led.h>
1093fdfc330SAdrian Chadd #include <dev/ath/if_ath_keycache.h>
1103fdfc330SAdrian Chadd #include <dev/ath/if_ath_rx.h>
1113fdfc330SAdrian Chadd #include <dev/ath/if_ath_beacon.h>
1123fdfc330SAdrian Chadd #include <dev/ath/if_athdfs.h>
1133fdfc330SAdrian Chadd 
1143fdfc330SAdrian Chadd #ifdef ATH_TX99_DIAG
1153fdfc330SAdrian Chadd #include <dev/ath/ath_tx99/ath_tx99.h>
1163fdfc330SAdrian Chadd #endif
1173fdfc330SAdrian Chadd 
1183fdfc330SAdrian Chadd #include <dev/ath/if_ath_tx_edma.h>
1193fdfc330SAdrian Chadd 
1203fdfc330SAdrian Chadd /*
1213fdfc330SAdrian Chadd  * some general macros
1223fdfc330SAdrian Chadd  */
1233fdfc330SAdrian Chadd #define	INCR(_l, _sz)		(_l) ++; (_l) &= ((_sz) - 1)
1243fdfc330SAdrian Chadd #define	DECR(_l, _sz)		(_l) --; (_l) &= ((_sz) - 1)
1253fdfc330SAdrian Chadd 
126ba3fd9d8SAdrian Chadd /*
127ba3fd9d8SAdrian Chadd  * XXX doesn't belong here, and should be tunable
128ba3fd9d8SAdrian Chadd  */
129ba3fd9d8SAdrian Chadd #define	ATH_TXSTATUS_RING_SIZE	512
130ba3fd9d8SAdrian Chadd 
1313fdfc330SAdrian Chadd MALLOC_DECLARE(M_ATHDEV);
1323fdfc330SAdrian Chadd 
133746bab5bSAdrian Chadd /*
134746bab5bSAdrian Chadd  * Re-initialise the DMA FIFO with the current contents of
135*3ae723d4SAdrian Chadd  * said TXQ.
136746bab5bSAdrian Chadd  *
137746bab5bSAdrian Chadd  * This should only be called as part of the chip reset path, as it
138746bab5bSAdrian Chadd  * assumes the FIFO is currently empty.
139746bab5bSAdrian Chadd  *
140746bab5bSAdrian Chadd  * TODO: verify that a cold/warm reset does clear the TX FIFO, so
141746bab5bSAdrian Chadd  * writing in a partially-filled FIFO will not cause double-entries
142746bab5bSAdrian Chadd  * to appear.
143746bab5bSAdrian Chadd  */
144746bab5bSAdrian Chadd static void
145746bab5bSAdrian Chadd ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq)
146746bab5bSAdrian Chadd {
147746bab5bSAdrian Chadd 
148746bab5bSAdrian Chadd 	device_printf(sc->sc_dev, "%s: called: txq=%p, qnum=%d\n",
149746bab5bSAdrian Chadd 	    __func__,
150746bab5bSAdrian Chadd 	    txq,
151746bab5bSAdrian Chadd 	    txq->axq_qnum);
152746bab5bSAdrian Chadd }
153746bab5bSAdrian Chadd 
154746bab5bSAdrian Chadd /*
155*3ae723d4SAdrian Chadd  * Hand off this frame to a hardware queue.
156*3ae723d4SAdrian Chadd  *
157*3ae723d4SAdrian Chadd  * Things are a bit hairy in the EDMA world.  The TX FIFO is only
158*3ae723d4SAdrian Chadd  * 8 entries deep, so we need to keep track of exactly what we've
159*3ae723d4SAdrian Chadd  * pushed into the FIFO and what's just sitting in the TX queue,
160*3ae723d4SAdrian Chadd  * waiting to go out.
161*3ae723d4SAdrian Chadd  *
162*3ae723d4SAdrian Chadd  * So this is split into two halves - frames get appended to the
163*3ae723d4SAdrian Chadd  * TXQ; then a scheduler is called to push some frames into the
164*3ae723d4SAdrian Chadd  * actual TX FIFO.
165*3ae723d4SAdrian Chadd  */
166*3ae723d4SAdrian Chadd static void
167*3ae723d4SAdrian Chadd ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq,
168*3ae723d4SAdrian Chadd     struct ath_buf *bf)
169*3ae723d4SAdrian Chadd {
170*3ae723d4SAdrian Chadd 	struct ath_hal *ah = sc->sc_ah;
171*3ae723d4SAdrian Chadd 
172*3ae723d4SAdrian Chadd 	ATH_TXQ_LOCK_ASSERT(txq);
173*3ae723d4SAdrian Chadd 
174*3ae723d4SAdrian Chadd 	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
175*3ae723d4SAdrian Chadd 	    ("%s: busy status 0x%x", __func__, bf->bf_flags));
176*3ae723d4SAdrian Chadd 
177*3ae723d4SAdrian Chadd 	/*
178*3ae723d4SAdrian Chadd 	 * XXX TODO: write a hard-coded check to ensure that
179*3ae723d4SAdrian Chadd 	 * the queue id in the TX descriptor matches txq->axq_qnum.
180*3ae723d4SAdrian Chadd 	 */
181*3ae723d4SAdrian Chadd 
182*3ae723d4SAdrian Chadd 	/* Update aggr stats */
183*3ae723d4SAdrian Chadd 	if (bf->bf_state.bfs_aggr)
184*3ae723d4SAdrian Chadd 		txq->axq_aggr_depth++;
185*3ae723d4SAdrian Chadd 
186*3ae723d4SAdrian Chadd 	/* Push and update frame stats */
187*3ae723d4SAdrian Chadd 	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
188*3ae723d4SAdrian Chadd 
189*3ae723d4SAdrian Chadd 	/* Only schedule to the FIFO if there's space */
190*3ae723d4SAdrian Chadd 	if (txq->axq_fifo_depth < HAL_TXFIFO_DEPTH) {
191*3ae723d4SAdrian Chadd 		ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
192*3ae723d4SAdrian Chadd 		ath_hal_txstart(ah, txq->axq_qnum);
193*3ae723d4SAdrian Chadd 	}
194*3ae723d4SAdrian Chadd }
195*3ae723d4SAdrian Chadd 
196*3ae723d4SAdrian Chadd /*
197*3ae723d4SAdrian Chadd  * Hand off this frame to a multicast software queue.
198*3ae723d4SAdrian Chadd  *
199*3ae723d4SAdrian Chadd  * Unlike legacy DMA, this doesn't chain together frames via the
200*3ae723d4SAdrian Chadd  * link pointer.  Instead, they're just added to the queue.
201*3ae723d4SAdrian Chadd  * When it comes time to populate the CABQ, these frames should
202*3ae723d4SAdrian Chadd  * be individually pushed into the FIFO as appropriate.
203*3ae723d4SAdrian Chadd  *
204*3ae723d4SAdrian Chadd  * Yes, this does mean that I'll eventually have to flesh out some
205*3ae723d4SAdrian Chadd  * replacement code to handle populating the CABQ, rather than
206*3ae723d4SAdrian Chadd  * what's done in ath_beacon_generate().  It'll have to push each
207*3ae723d4SAdrian Chadd  * frame from the HW CABQ to the FIFO rather than just appending
208*3ae723d4SAdrian Chadd  * it to the existing TXQ and kicking off DMA.
209*3ae723d4SAdrian Chadd  */
210*3ae723d4SAdrian Chadd static void
211*3ae723d4SAdrian Chadd ath_edma_xmit_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq,
212*3ae723d4SAdrian Chadd     struct ath_buf *bf)
213*3ae723d4SAdrian Chadd {
214*3ae723d4SAdrian Chadd 
215*3ae723d4SAdrian Chadd 	ATH_TXQ_LOCK_ASSERT(txq);
216*3ae723d4SAdrian Chadd 	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
217*3ae723d4SAdrian Chadd 	    ("%s: busy status 0x%x", __func__, bf->bf_flags));
218*3ae723d4SAdrian Chadd 
219*3ae723d4SAdrian Chadd 	/*
220*3ae723d4SAdrian Chadd 	 * XXX this is mostly duplicated in ath_tx_handoff_mcast().
221*3ae723d4SAdrian Chadd 	 */
222*3ae723d4SAdrian Chadd 	if (ATH_TXQ_FIRST(txq) != NULL) {
223*3ae723d4SAdrian Chadd 		struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s);
224*3ae723d4SAdrian Chadd 		struct ieee80211_frame *wh;
225*3ae723d4SAdrian Chadd 
226*3ae723d4SAdrian Chadd 		/* mark previous frame */
227*3ae723d4SAdrian Chadd 		wh = mtod(bf_last->bf_m, struct ieee80211_frame *);
228*3ae723d4SAdrian Chadd 		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
229*3ae723d4SAdrian Chadd 
230*3ae723d4SAdrian Chadd 		/* sync descriptor to memory */
231*3ae723d4SAdrian Chadd 		bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap,
232*3ae723d4SAdrian Chadd 		   BUS_DMASYNC_PREWRITE);
233*3ae723d4SAdrian Chadd 	}
234*3ae723d4SAdrian Chadd 
235*3ae723d4SAdrian Chadd 	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
236*3ae723d4SAdrian Chadd }
237*3ae723d4SAdrian Chadd 
238*3ae723d4SAdrian Chadd /*
239746bab5bSAdrian Chadd  * Handoff this frame to the hardware.
240746bab5bSAdrian Chadd  *
241746bab5bSAdrian Chadd  * For the multicast queue, this will treat it as a software queue
242746bab5bSAdrian Chadd  * and append it to the list, after updating the MORE_DATA flag
243746bab5bSAdrian Chadd  * in the previous frame.  The cabq processing code will ensure
244746bab5bSAdrian Chadd  * that the queue contents gets transferred over.
245746bab5bSAdrian Chadd  *
246746bab5bSAdrian Chadd  * For the hardware queues, this will queue a frame to the queue
247746bab5bSAdrian Chadd  * like before, then populate the FIFO from that.  Since the
248746bab5bSAdrian Chadd  * EDMA hardware has 8 FIFO slots per TXQ, this ensures that
249746bab5bSAdrian Chadd  * frames such as management frames don't get prematurely dropped.
250746bab5bSAdrian Chadd  *
251746bab5bSAdrian Chadd  * This does imply that a similar flush-hwq-to-fifoq method will
252746bab5bSAdrian Chadd  * need to be called from the processq function, before the
253746bab5bSAdrian Chadd  * per-node software scheduler is called.
254746bab5bSAdrian Chadd  */
255746bab5bSAdrian Chadd static void
256746bab5bSAdrian Chadd ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq,
257746bab5bSAdrian Chadd     struct ath_buf *bf)
258746bab5bSAdrian Chadd {
259746bab5bSAdrian Chadd 
260*3ae723d4SAdrian Chadd 	ATH_TXQ_LOCK_ASSERT(txq);
261*3ae723d4SAdrian Chadd 
262746bab5bSAdrian Chadd 	device_printf(sc->sc_dev, "%s: called; bf=%p, txq=%p, qnum=%d\n",
263746bab5bSAdrian Chadd 	    __func__,
264746bab5bSAdrian Chadd 	    bf,
265746bab5bSAdrian Chadd 	    txq,
266746bab5bSAdrian Chadd 	    txq->axq_qnum);
267746bab5bSAdrian Chadd 
268*3ae723d4SAdrian Chadd 	if (txq->axq_qnum == ATH_TXQ_SWQ)
269*3ae723d4SAdrian Chadd 		ath_edma_xmit_handoff_mcast(sc, txq, bf);
270*3ae723d4SAdrian Chadd 	else
271*3ae723d4SAdrian Chadd 		ath_edma_xmit_handoff_hw(sc, txq, bf);
272*3ae723d4SAdrian Chadd 
273*3ae723d4SAdrian Chadd #if 0
274746bab5bSAdrian Chadd 	/*
275746bab5bSAdrian Chadd 	 * XXX For now this is a placeholder; free the buffer
276746bab5bSAdrian Chadd 	 * and inform the stack that the TX failed.
277746bab5bSAdrian Chadd 	 */
278746bab5bSAdrian Chadd 	ath_tx_default_comp(sc, bf, 1);
279*3ae723d4SAdrian Chadd #endif
280746bab5bSAdrian Chadd }
281746bab5bSAdrian Chadd 
2823fdfc330SAdrian Chadd static int
28379607afeSAdrian Chadd ath_edma_setup_txfifo(struct ath_softc *sc, int qnum)
28479607afeSAdrian Chadd {
28579607afeSAdrian Chadd 	struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum];
28679607afeSAdrian Chadd 
28779607afeSAdrian Chadd 	te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH,
28879607afeSAdrian Chadd 	    M_ATHDEV,
28979607afeSAdrian Chadd 	    M_NOWAIT | M_ZERO);
29079607afeSAdrian Chadd 	if (te->m_fifo == NULL) {
29179607afeSAdrian Chadd 		device_printf(sc->sc_dev, "%s: malloc failed\n",
29279607afeSAdrian Chadd 		    __func__);
29379607afeSAdrian Chadd 		return (-ENOMEM);
29479607afeSAdrian Chadd 	}
29579607afeSAdrian Chadd 
29679607afeSAdrian Chadd 	/*
29779607afeSAdrian Chadd 	 * Set initial "empty" state.
29879607afeSAdrian Chadd 	 */
29979607afeSAdrian Chadd 	te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0;
30079607afeSAdrian Chadd 
30179607afeSAdrian Chadd 	return (0);
30279607afeSAdrian Chadd }
30379607afeSAdrian Chadd 
30479607afeSAdrian Chadd static int
30579607afeSAdrian Chadd ath_edma_free_txfifo(struct ath_softc *sc, int qnum)
30679607afeSAdrian Chadd {
30779607afeSAdrian Chadd 	struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum];
30879607afeSAdrian Chadd 
30979607afeSAdrian Chadd 	/* XXX TODO: actually deref the ath_buf entries? */
31079607afeSAdrian Chadd 	free(te->m_fifo, M_ATHDEV);
31179607afeSAdrian Chadd 	return (0);
31279607afeSAdrian Chadd }
31379607afeSAdrian Chadd 
31479607afeSAdrian Chadd static int
3153fdfc330SAdrian Chadd ath_edma_dma_txsetup(struct ath_softc *sc)
3163fdfc330SAdrian Chadd {
317ba3fd9d8SAdrian Chadd 	int error;
31879607afeSAdrian Chadd 	int i;
3193fdfc330SAdrian Chadd 
320ba3fd9d8SAdrian Chadd 	error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma,
321ba3fd9d8SAdrian Chadd 	    NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE);
322ba3fd9d8SAdrian Chadd 	if (error != 0)
323ba3fd9d8SAdrian Chadd 		return (error);
324ba3fd9d8SAdrian Chadd 
325ba3fd9d8SAdrian Chadd 	ath_hal_setuptxstatusring(sc->sc_ah,
326ba3fd9d8SAdrian Chadd 	    (void *) sc->sc_txsdma.dd_desc,
327ba3fd9d8SAdrian Chadd 	    sc->sc_txsdma.dd_desc_paddr,
328ba3fd9d8SAdrian Chadd 	    ATH_TXSTATUS_RING_SIZE);
329ba3fd9d8SAdrian Chadd 
33079607afeSAdrian Chadd 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
33179607afeSAdrian Chadd 		ath_edma_setup_txfifo(sc, i);
33279607afeSAdrian Chadd 	}
33379607afeSAdrian Chadd 
334ba3fd9d8SAdrian Chadd 
3353fdfc330SAdrian Chadd 	return (0);
3363fdfc330SAdrian Chadd }
3373fdfc330SAdrian Chadd 
3383fdfc330SAdrian Chadd static int
3393fdfc330SAdrian Chadd ath_edma_dma_txteardown(struct ath_softc *sc)
3403fdfc330SAdrian Chadd {
34179607afeSAdrian Chadd 	int i;
34279607afeSAdrian Chadd 
34379607afeSAdrian Chadd 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
34479607afeSAdrian Chadd 		ath_edma_free_txfifo(sc, i);
34579607afeSAdrian Chadd 	}
3463fdfc330SAdrian Chadd 
347ba3fd9d8SAdrian Chadd 	ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL);
3483fdfc330SAdrian Chadd 	return (0);
3493fdfc330SAdrian Chadd }
3503fdfc330SAdrian Chadd 
351*3ae723d4SAdrian Chadd /*
352*3ae723d4SAdrian Chadd  * Process frames in the current queue and if necessary, re-schedule the
353*3ae723d4SAdrian Chadd  * software TXQ scheduler for this TXQ.
354*3ae723d4SAdrian Chadd  *
355*3ae723d4SAdrian Chadd  * XXX This is again a pain in the ass to do because the status descriptor
356*3ae723d4SAdrian Chadd  * information is in the TX status FIFO, not with the current descriptor.
357*3ae723d4SAdrian Chadd  */
358f8418db5SAdrian Chadd static int
359f8418db5SAdrian Chadd ath_edma_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched)
360f8418db5SAdrian Chadd {
361f8418db5SAdrian Chadd 
362*3ae723d4SAdrian Chadd 	device_printf(sc->sc_dev, "%s: called\n", __func__);
363f8418db5SAdrian Chadd 	return (0);
364f8418db5SAdrian Chadd }
365f8418db5SAdrian Chadd 
366*3ae723d4SAdrian Chadd /*
367*3ae723d4SAdrian Chadd  * Completely drain the TXQ, completing frames that were completed.
368*3ae723d4SAdrian Chadd  *
369*3ae723d4SAdrian Chadd  * XXX this is going to be a complete pain in the ass because the
370*3ae723d4SAdrian Chadd  * completion status is in the TX status FIFO, not with the descriptor
371*3ae723d4SAdrian Chadd  * itself.  Sigh.
372*3ae723d4SAdrian Chadd  */
373f8418db5SAdrian Chadd static void
374f8418db5SAdrian Chadd ath_edma_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
375f8418db5SAdrian Chadd {
376f8418db5SAdrian Chadd 
377*3ae723d4SAdrian Chadd 	device_printf(sc->sc_dev, "%s: called\n", __func__);
378f8418db5SAdrian Chadd }
379f8418db5SAdrian Chadd 
380*3ae723d4SAdrian Chadd /*
381*3ae723d4SAdrian Chadd  * Process the TX status queue.
382*3ae723d4SAdrian Chadd  */
383f8418db5SAdrian Chadd static void
384f8418db5SAdrian Chadd ath_edma_tx_proc(void *arg, int npending)
385f8418db5SAdrian Chadd {
386f8418db5SAdrian Chadd 	struct ath_softc *sc = (struct ath_softc *) arg;
387*3ae723d4SAdrian Chadd 	struct ath_hal *ah = sc->sc_ah;
388*3ae723d4SAdrian Chadd 	HAL_STATUS status;
389*3ae723d4SAdrian Chadd 	struct ath_tx_status ts;
390*3ae723d4SAdrian Chadd 	struct ath_txq *txq;
391f8418db5SAdrian Chadd 
392f8418db5SAdrian Chadd 	device_printf(sc->sc_dev, "%s: called, npending=%d\n",
393f8418db5SAdrian Chadd 	    __func__, npending);
394*3ae723d4SAdrian Chadd 
395*3ae723d4SAdrian Chadd 	for (;;) {
396*3ae723d4SAdrian Chadd 		ATH_TXSTATUS_LOCK(sc);
397*3ae723d4SAdrian Chadd 		status = ath_hal_txprocdesc(ah, NULL, (void *) &ts);
398*3ae723d4SAdrian Chadd 		ATH_TXSTATUS_UNLOCK(sc);
399*3ae723d4SAdrian Chadd 
400*3ae723d4SAdrian Chadd 		if (status != HAL_OK)
401*3ae723d4SAdrian Chadd 			break;
402*3ae723d4SAdrian Chadd 
403*3ae723d4SAdrian Chadd 		/*
404*3ae723d4SAdrian Chadd 		 * At this point we have a valid status descriptor.
405*3ae723d4SAdrian Chadd 		 * The QID and descriptor ID (which currently isn't set)
406*3ae723d4SAdrian Chadd 		 * is part of the status.
407*3ae723d4SAdrian Chadd 		 *
408*3ae723d4SAdrian Chadd 		 * We then assume that the descriptor in question is the
409*3ae723d4SAdrian Chadd 		 * -head- of the given QID.  Eventually we should verify
410*3ae723d4SAdrian Chadd 		 * this by using the descriptor ID.
411*3ae723d4SAdrian Chadd 		 */
412*3ae723d4SAdrian Chadd 		device_printf(sc->sc_dev, "%s: qcuid=%d\n",
413*3ae723d4SAdrian Chadd 		    __func__,
414*3ae723d4SAdrian Chadd 		    ts.ts_queue_id);
415*3ae723d4SAdrian Chadd 
416*3ae723d4SAdrian Chadd 		txq = &sc->sc_txq[ts.ts_queue_id];
417*3ae723d4SAdrian Chadd 	}
418f8418db5SAdrian Chadd }
419f8418db5SAdrian Chadd 
420f8418db5SAdrian Chadd static void
421f8418db5SAdrian Chadd ath_edma_attach_comp_func(struct ath_softc *sc)
422f8418db5SAdrian Chadd {
423f8418db5SAdrian Chadd 
424f8418db5SAdrian Chadd 	TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc);
425f8418db5SAdrian Chadd }
426f8418db5SAdrian Chadd 
4273fdfc330SAdrian Chadd void
4283fdfc330SAdrian Chadd ath_xmit_setup_edma(struct ath_softc *sc)
4293fdfc330SAdrian Chadd {
4303fdfc330SAdrian Chadd 
4313fdfc330SAdrian Chadd 	/* Fetch EDMA field and buffer sizes */
4323fdfc330SAdrian Chadd 	(void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen);
4333fdfc330SAdrian Chadd 	(void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen);
4343fdfc330SAdrian Chadd 	(void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps);
4353fdfc330SAdrian Chadd 
4363fdfc330SAdrian Chadd 	device_printf(sc->sc_dev, "TX descriptor length: %d\n",
4373fdfc330SAdrian Chadd 	    sc->sc_tx_desclen);
4383fdfc330SAdrian Chadd 	device_printf(sc->sc_dev, "TX status length: %d\n",
4393fdfc330SAdrian Chadd 	    sc->sc_tx_statuslen);
4403fdfc330SAdrian Chadd 	device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n",
4413fdfc330SAdrian Chadd 	    sc->sc_tx_nmaps);
4423fdfc330SAdrian Chadd 
4433fdfc330SAdrian Chadd 	sc->sc_tx.xmit_setup = ath_edma_dma_txsetup;
4443fdfc330SAdrian Chadd 	sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown;
445f8418db5SAdrian Chadd 	sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func;
446746bab5bSAdrian Chadd 
447746bab5bSAdrian Chadd 	sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart;
448746bab5bSAdrian Chadd 	sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff;
449f8418db5SAdrian Chadd 	sc->sc_tx.xmit_processq = ath_edma_tx_processq;
450f8418db5SAdrian Chadd 	sc->sc_tx.xmit_drainq = ath_edma_tx_draintxq;
4513fdfc330SAdrian Chadd }
452