105491d2cSKalle Valo /*
205491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
305491d2cSKalle Valo  *
405491d2cSKalle Valo  * Permission to use, copy, modify, and/or distribute this software for any
505491d2cSKalle Valo  * purpose with or without fee is hereby granted, provided that the above
605491d2cSKalle Valo  * copyright notice and this permission notice appear in all copies.
705491d2cSKalle Valo  *
805491d2cSKalle Valo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
905491d2cSKalle Valo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1005491d2cSKalle Valo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1105491d2cSKalle Valo  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1205491d2cSKalle Valo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1305491d2cSKalle Valo  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1405491d2cSKalle Valo  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1505491d2cSKalle Valo  */
1605491d2cSKalle Valo #include <net/mac80211.h>
1705491d2cSKalle Valo 
1805491d2cSKalle Valo #include "rate.h"
1905491d2cSKalle Valo #include "scb.h"
2005491d2cSKalle Valo #include "phy/phy_hal.h"
2105491d2cSKalle Valo #include "antsel.h"
2205491d2cSKalle Valo #include "main.h"
2305491d2cSKalle Valo #include "ampdu.h"
2405491d2cSKalle Valo #include "debug.h"
2505491d2cSKalle Valo #include "brcms_trace_events.h"
2605491d2cSKalle Valo 
2705491d2cSKalle Valo /* max number of mpdus in an ampdu */
2805491d2cSKalle Valo #define AMPDU_MAX_MPDU			32
2905491d2cSKalle Valo /* max number of mpdus in an ampdu to a legacy */
3005491d2cSKalle Valo #define AMPDU_NUM_MPDU_LEGACY		16
3105491d2cSKalle Valo /* max Tx ba window size (in pdu) */
3205491d2cSKalle Valo #define AMPDU_TX_BA_MAX_WSIZE		64
3305491d2cSKalle Valo /* default Tx ba window size (in pdu) */
3405491d2cSKalle Valo #define AMPDU_TX_BA_DEF_WSIZE		64
3505491d2cSKalle Valo /* default Rx ba window size (in pdu) */
3605491d2cSKalle Valo #define AMPDU_RX_BA_DEF_WSIZE		64
3705491d2cSKalle Valo /* max Rx ba window size (in pdu) */
3805491d2cSKalle Valo #define AMPDU_RX_BA_MAX_WSIZE		64
3905491d2cSKalle Valo /* max dur of tx ampdu (in msec) */
4005491d2cSKalle Valo #define	AMPDU_MAX_DUR			5
4105491d2cSKalle Valo /* default tx retry limit */
4205491d2cSKalle Valo #define AMPDU_DEF_RETRY_LIMIT		5
4305491d2cSKalle Valo /* default tx retry limit at reg rate */
4405491d2cSKalle Valo #define AMPDU_DEF_RR_RETRY_LIMIT	2
4505491d2cSKalle Valo /* default ffpld reserved bytes */
4605491d2cSKalle Valo #define AMPDU_DEF_FFPLD_RSVD		2048
4705491d2cSKalle Valo /* # of inis to be freed on detach */
4805491d2cSKalle Valo #define AMPDU_INI_FREE			10
4905491d2cSKalle Valo /* max # of mpdus released at a time */
5005491d2cSKalle Valo #define	AMPDU_SCB_MAX_RELEASE		20
5105491d2cSKalle Valo 
5205491d2cSKalle Valo #define NUM_FFPLD_FIFO 4	/* number of fifo concerned by pre-loading */
5305491d2cSKalle Valo #define FFPLD_TX_MAX_UNFL   200	/* default value of the average number of ampdu
5405491d2cSKalle Valo 				 * without underflows
5505491d2cSKalle Valo 				 */
5605491d2cSKalle Valo #define FFPLD_MPDU_SIZE 1800	/* estimate of maximum mpdu size */
5705491d2cSKalle Valo #define FFPLD_MAX_MCS 23	/* we don't deal with mcs 32 */
5805491d2cSKalle Valo #define FFPLD_PLD_INCR 1000	/* increments in bytes */
5905491d2cSKalle Valo #define FFPLD_MAX_AMPDU_CNT 5000	/* maximum number of ampdu we
6005491d2cSKalle Valo 					 * accumulate between resets.
6105491d2cSKalle Valo 					 */
6205491d2cSKalle Valo 
6305491d2cSKalle Valo #define AMPDU_DELIMITER_LEN	4
6405491d2cSKalle Valo 
6505491d2cSKalle Valo /* max allowed number of mpdus in an ampdu (2 streams) */
6605491d2cSKalle Valo #define AMPDU_NUM_MPDU		16
6705491d2cSKalle Valo 
6805491d2cSKalle Valo #define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
6905491d2cSKalle Valo 
7005491d2cSKalle Valo /* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
7105491d2cSKalle Valo #define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
7205491d2cSKalle Valo 	AMPDU_DELIMITER_LEN + 3\
7305491d2cSKalle Valo 	+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
7405491d2cSKalle Valo 
7505491d2cSKalle Valo /* modulo add/sub, bound = 2^k */
7605491d2cSKalle Valo #define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1))
7705491d2cSKalle Valo #define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1))
7805491d2cSKalle Valo 
7905491d2cSKalle Valo /* structure to hold tx fifo information and pre-loading state
8005491d2cSKalle Valo  * counters specific to tx underflows of ampdus
8105491d2cSKalle Valo  * some counters might be redundant with the ones in wlc or ampdu structures.
8205491d2cSKalle Valo  * This allows to maintain a specific state independently of
8305491d2cSKalle Valo  * how often and/or when the wlc counters are updated.
8405491d2cSKalle Valo  *
8505491d2cSKalle Valo  * ampdu_pld_size: number of bytes to be pre-loaded
8605491d2cSKalle Valo  * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu
8705491d2cSKalle Valo  * prev_txfunfl: num of underflows last read from the HW macstats counter
8805491d2cSKalle Valo  * accum_txfunfl: num of underflows since we modified pld params
8905491d2cSKalle Valo  * accum_txampdu: num of tx ampdu since we modified pld params
9005491d2cSKalle Valo  * prev_txampdu: previous reading of tx ampdu
9105491d2cSKalle Valo  * dmaxferrate: estimated dma avg xfer rate in kbits/sec
9205491d2cSKalle Valo  */
9305491d2cSKalle Valo struct brcms_fifo_info {
9405491d2cSKalle Valo 	u16 ampdu_pld_size;
9505491d2cSKalle Valo 	u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1];
9605491d2cSKalle Valo 	u16 prev_txfunfl;
9705491d2cSKalle Valo 	u32 accum_txfunfl;
9805491d2cSKalle Valo 	u32 accum_txampdu;
9905491d2cSKalle Valo 	u32 prev_txampdu;
10005491d2cSKalle Valo 	u32 dmaxferrate;
10105491d2cSKalle Valo };
10205491d2cSKalle Valo 
10305491d2cSKalle Valo /* AMPDU module specific state
10405491d2cSKalle Valo  *
10505491d2cSKalle Valo  * wlc: pointer to main wlc structure
10605491d2cSKalle Valo  * scb_handle: scb cubby handle to retrieve data from scb
10705491d2cSKalle Valo  * ini_enable: per-tid initiator enable/disable of ampdu
10805491d2cSKalle Valo  * ba_tx_wsize: Tx ba window size (in pdu)
10905491d2cSKalle Valo  * ba_rx_wsize: Rx ba window size (in pdu)
11005491d2cSKalle Valo  * retry_limit: mpdu transmit retry limit
11105491d2cSKalle Valo  * rr_retry_limit: mpdu transmit retry limit at regular rate
11205491d2cSKalle Valo  * retry_limit_tid: per-tid mpdu transmit retry limit
11305491d2cSKalle Valo  * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate
11405491d2cSKalle Valo  * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec
11505491d2cSKalle Valo  * max_pdu: max pdus allowed in ampdu
11605491d2cSKalle Valo  * dur: max duration of an ampdu (in msec)
11705491d2cSKalle Valo  * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes
11805491d2cSKalle Valo  * ffpld_rsvd: number of bytes to reserve for preload
11905491d2cSKalle Valo  * max_txlen: max size of ampdu per mcs, bw and sgi
12005491d2cSKalle Valo  * mfbr: enable multiple fallback rate
12105491d2cSKalle Valo  * tx_max_funl: underflows should be kept such that
12205491d2cSKalle Valo  *		(tx_max_funfl*underflows) < tx frames
12305491d2cSKalle Valo  * fifo_tb: table of fifo infos
12405491d2cSKalle Valo  */
12505491d2cSKalle Valo struct ampdu_info {
12605491d2cSKalle Valo 	struct brcms_c_info *wlc;
12705491d2cSKalle Valo 	int scb_handle;
12805491d2cSKalle Valo 	u8 ini_enable[AMPDU_MAX_SCB_TID];
12905491d2cSKalle Valo 	u8 ba_tx_wsize;
13005491d2cSKalle Valo 	u8 ba_rx_wsize;
13105491d2cSKalle Valo 	u8 retry_limit;
13205491d2cSKalle Valo 	u8 rr_retry_limit;
13305491d2cSKalle Valo 	u8 retry_limit_tid[AMPDU_MAX_SCB_TID];
13405491d2cSKalle Valo 	u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
13505491d2cSKalle Valo 	u8 mpdu_density;
13605491d2cSKalle Valo 	s8 max_pdu;
13705491d2cSKalle Valo 	u8 dur;
13805491d2cSKalle Valo 	u8 rx_factor;
13905491d2cSKalle Valo 	u32 ffpld_rsvd;
14005491d2cSKalle Valo 	u32 max_txlen[MCS_TABLE_SIZE][2][2];
14105491d2cSKalle Valo 	bool mfbr;
14205491d2cSKalle Valo 	u32 tx_max_funl;
14305491d2cSKalle Valo 	struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO];
14405491d2cSKalle Valo };
14505491d2cSKalle Valo 
14605491d2cSKalle Valo /* used for flushing ampdu packets */
14705491d2cSKalle Valo struct cb_del_ampdu_pars {
14805491d2cSKalle Valo 	struct ieee80211_sta *sta;
14905491d2cSKalle Valo 	u16 tid;
15005491d2cSKalle Valo };
15105491d2cSKalle Valo 
brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info * ampdu,u8 dur)15205491d2cSKalle Valo static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
15305491d2cSKalle Valo {
15405491d2cSKalle Valo 	u32 rate, mcs;
15505491d2cSKalle Valo 
15605491d2cSKalle Valo 	for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
15705491d2cSKalle Valo 		/* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
15805491d2cSKalle Valo 		/* 20MHz, No SGI */
15905491d2cSKalle Valo 		rate = mcs_2_rate(mcs, false, false);
16005491d2cSKalle Valo 		ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
16105491d2cSKalle Valo 		/* 40 MHz, No SGI */
16205491d2cSKalle Valo 		rate = mcs_2_rate(mcs, true, false);
16305491d2cSKalle Valo 		ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
16405491d2cSKalle Valo 		/* 20MHz, SGI */
16505491d2cSKalle Valo 		rate = mcs_2_rate(mcs, false, true);
16605491d2cSKalle Valo 		ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
16705491d2cSKalle Valo 		/* 40 MHz, SGI */
16805491d2cSKalle Valo 		rate = mcs_2_rate(mcs, true, true);
16905491d2cSKalle Valo 		ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
17005491d2cSKalle Valo 	}
17105491d2cSKalle Valo }
17205491d2cSKalle Valo 
brcms_c_ampdu_cap(struct ampdu_info * ampdu)17305491d2cSKalle Valo static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu)
17405491d2cSKalle Valo {
17505491d2cSKalle Valo 	if (BRCMS_PHY_11N_CAP(ampdu->wlc->band))
17605491d2cSKalle Valo 		return true;
17705491d2cSKalle Valo 	else
17805491d2cSKalle Valo 		return false;
17905491d2cSKalle Valo }
18005491d2cSKalle Valo 
brcms_c_ampdu_set(struct ampdu_info * ampdu,bool on)18105491d2cSKalle Valo static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on)
18205491d2cSKalle Valo {
18305491d2cSKalle Valo 	struct brcms_c_info *wlc = ampdu->wlc;
18405491d2cSKalle Valo 	struct bcma_device *core = wlc->hw->d11core;
18505491d2cSKalle Valo 
18605491d2cSKalle Valo 	wlc->pub->_ampdu = false;
18705491d2cSKalle Valo 
18805491d2cSKalle Valo 	if (on) {
18905491d2cSKalle Valo 		if (!(wlc->pub->_n_enab & SUPPORT_11N)) {
19005491d2cSKalle Valo 			brcms_err(core, "wl%d: driver not nmode enabled\n",
19105491d2cSKalle Valo 				  wlc->pub->unit);
19205491d2cSKalle Valo 			return -ENOTSUPP;
19305491d2cSKalle Valo 		}
19405491d2cSKalle Valo 		if (!brcms_c_ampdu_cap(ampdu)) {
19505491d2cSKalle Valo 			brcms_err(core, "wl%d: device not ampdu capable\n",
19605491d2cSKalle Valo 				  wlc->pub->unit);
19705491d2cSKalle Valo 			return -ENOTSUPP;
19805491d2cSKalle Valo 		}
19905491d2cSKalle Valo 		wlc->pub->_ampdu = on;
20005491d2cSKalle Valo 	}
20105491d2cSKalle Valo 
20205491d2cSKalle Valo 	return 0;
20305491d2cSKalle Valo }
20405491d2cSKalle Valo 
brcms_c_ffpld_init(struct ampdu_info * ampdu)20505491d2cSKalle Valo static void brcms_c_ffpld_init(struct ampdu_info *ampdu)
20605491d2cSKalle Valo {
20705491d2cSKalle Valo 	int i, j;
20805491d2cSKalle Valo 	struct brcms_fifo_info *fifo;
20905491d2cSKalle Valo 
21005491d2cSKalle Valo 	for (j = 0; j < NUM_FFPLD_FIFO; j++) {
21105491d2cSKalle Valo 		fifo = (ampdu->fifo_tb + j);
21205491d2cSKalle Valo 		fifo->ampdu_pld_size = 0;
21305491d2cSKalle Valo 		for (i = 0; i <= FFPLD_MAX_MCS; i++)
21405491d2cSKalle Valo 			fifo->mcs2ampdu_table[i] = 255;
21505491d2cSKalle Valo 		fifo->dmaxferrate = 0;
21605491d2cSKalle Valo 		fifo->accum_txampdu = 0;
21705491d2cSKalle Valo 		fifo->prev_txfunfl = 0;
21805491d2cSKalle Valo 		fifo->accum_txfunfl = 0;
21905491d2cSKalle Valo 
22005491d2cSKalle Valo 	}
22105491d2cSKalle Valo }
22205491d2cSKalle Valo 
brcms_c_ampdu_attach(struct brcms_c_info * wlc)22305491d2cSKalle Valo struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc)
22405491d2cSKalle Valo {
22505491d2cSKalle Valo 	struct ampdu_info *ampdu;
22605491d2cSKalle Valo 	int i;
22705491d2cSKalle Valo 
22805491d2cSKalle Valo 	ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
22905491d2cSKalle Valo 	if (!ampdu)
23005491d2cSKalle Valo 		return NULL;
23105491d2cSKalle Valo 
23205491d2cSKalle Valo 	ampdu->wlc = wlc;
23305491d2cSKalle Valo 
23405491d2cSKalle Valo 	for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
23505491d2cSKalle Valo 		ampdu->ini_enable[i] = true;
23605491d2cSKalle Valo 	/* Disable ampdu for VO by default */
23705491d2cSKalle Valo 	ampdu->ini_enable[PRIO_8021D_VO] = false;
23805491d2cSKalle Valo 	ampdu->ini_enable[PRIO_8021D_NC] = false;
23905491d2cSKalle Valo 
24005491d2cSKalle Valo 	/* Disable ampdu for BK by default since not enough fifo space */
24105491d2cSKalle Valo 	ampdu->ini_enable[PRIO_8021D_NONE] = false;
24205491d2cSKalle Valo 	ampdu->ini_enable[PRIO_8021D_BK] = false;
24305491d2cSKalle Valo 
24405491d2cSKalle Valo 	ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
24505491d2cSKalle Valo 	ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
24605491d2cSKalle Valo 	ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
24705491d2cSKalle Valo 	ampdu->max_pdu = AUTO;
24805491d2cSKalle Valo 	ampdu->dur = AMPDU_MAX_DUR;
24905491d2cSKalle Valo 
25005491d2cSKalle Valo 	ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
25105491d2cSKalle Valo 	/*
25205491d2cSKalle Valo 	 * bump max ampdu rcv size to 64k for all 11n
25305491d2cSKalle Valo 	 * devices except 4321A0 and 4321A1
25405491d2cSKalle Valo 	 */
25505491d2cSKalle Valo 	if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
25605491d2cSKalle Valo 		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K;
25705491d2cSKalle Valo 	else
25805491d2cSKalle Valo 		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
25905491d2cSKalle Valo 	ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
26005491d2cSKalle Valo 	ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
26105491d2cSKalle Valo 
26205491d2cSKalle Valo 	for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
26305491d2cSKalle Valo 		ampdu->retry_limit_tid[i] = ampdu->retry_limit;
26405491d2cSKalle Valo 		ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
26505491d2cSKalle Valo 	}
26605491d2cSKalle Valo 
26705491d2cSKalle Valo 	brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur);
26805491d2cSKalle Valo 	ampdu->mfbr = false;
26905491d2cSKalle Valo 	/* try to set ampdu to the default value */
27005491d2cSKalle Valo 	brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu);
27105491d2cSKalle Valo 
27205491d2cSKalle Valo 	ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
27305491d2cSKalle Valo 	brcms_c_ffpld_init(ampdu);
27405491d2cSKalle Valo 
27505491d2cSKalle Valo 	return ampdu;
27605491d2cSKalle Valo }
27705491d2cSKalle Valo 
brcms_c_ampdu_detach(struct ampdu_info * ampdu)27805491d2cSKalle Valo void brcms_c_ampdu_detach(struct ampdu_info *ampdu)
27905491d2cSKalle Valo {
28005491d2cSKalle Valo 	kfree(ampdu);
28105491d2cSKalle Valo }
28205491d2cSKalle Valo 
brcms_c_scb_ampdu_update_config(struct ampdu_info * ampdu,struct scb * scb)28305491d2cSKalle Valo static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu,
28405491d2cSKalle Valo 					    struct scb *scb)
28505491d2cSKalle Valo {
28605491d2cSKalle Valo 	struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
28705491d2cSKalle Valo 	int i;
28805491d2cSKalle Valo 
28905491d2cSKalle Valo 	scb_ampdu->max_pdu = AMPDU_NUM_MPDU;
29005491d2cSKalle Valo 
29105491d2cSKalle Valo 	/* go back to legacy size if some preloading is occurring */
29205491d2cSKalle Valo 	for (i = 0; i < NUM_FFPLD_FIFO; i++) {
29305491d2cSKalle Valo 		if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
29405491d2cSKalle Valo 			scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
29505491d2cSKalle Valo 	}
29605491d2cSKalle Valo 
29705491d2cSKalle Valo 	/* apply user override */
29805491d2cSKalle Valo 	if (ampdu->max_pdu != AUTO)
29905491d2cSKalle Valo 		scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
30005491d2cSKalle Valo 
30105491d2cSKalle Valo 	scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu,
30205491d2cSKalle Valo 				   AMPDU_SCB_MAX_RELEASE);
30305491d2cSKalle Valo 
30405491d2cSKalle Valo 	if (scb_ampdu->max_rx_ampdu_bytes)
30505491d2cSKalle Valo 		scb_ampdu->release = min_t(u8, scb_ampdu->release,
30605491d2cSKalle Valo 			scb_ampdu->max_rx_ampdu_bytes / 1600);
30705491d2cSKalle Valo 
30805491d2cSKalle Valo 	scb_ampdu->release = min(scb_ampdu->release,
30905491d2cSKalle Valo 				 ampdu->fifo_tb[TX_AC_BE_FIFO].
31005491d2cSKalle Valo 				 mcs2ampdu_table[FFPLD_MAX_MCS]);
31105491d2cSKalle Valo }
31205491d2cSKalle Valo 
brcms_c_scb_ampdu_update_config_all(struct ampdu_info * ampdu)31305491d2cSKalle Valo static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu)
31405491d2cSKalle Valo {
31505491d2cSKalle Valo 	brcms_c_scb_ampdu_update_config(ampdu, &ampdu->wlc->pri_scb);
31605491d2cSKalle Valo }
31705491d2cSKalle Valo 
brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info * ampdu,int f)31805491d2cSKalle Valo static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
31905491d2cSKalle Valo {
32005491d2cSKalle Valo 	int i;
32105491d2cSKalle Valo 	u32 phy_rate, dma_rate, tmp;
32205491d2cSKalle Valo 	u8 max_mpdu;
32305491d2cSKalle Valo 	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f);
32405491d2cSKalle Valo 
32505491d2cSKalle Valo 	/* recompute the dma rate */
32605491d2cSKalle Valo 	/* note : we divide/multiply by 100 to avoid integer overflows */
32705491d2cSKalle Valo 	max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
32805491d2cSKalle Valo 			 AMPDU_NUM_MPDU_LEGACY);
32905491d2cSKalle Valo 	phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
33005491d2cSKalle Valo 	dma_rate =
33105491d2cSKalle Valo 	    (((phy_rate / 100) *
33205491d2cSKalle Valo 	      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
33305491d2cSKalle Valo 	     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
33405491d2cSKalle Valo 	fifo->dmaxferrate = dma_rate;
33505491d2cSKalle Valo 
33605491d2cSKalle Valo 	/* fill up the mcs2ampdu table; do not recalc the last mcs */
33705491d2cSKalle Valo 	dma_rate = dma_rate >> 7;
33805491d2cSKalle Valo 	for (i = 0; i < FFPLD_MAX_MCS; i++) {
33905491d2cSKalle Valo 		/* shifting to keep it within integer range */
34005491d2cSKalle Valo 		phy_rate = mcs_2_rate(i, true, false) >> 7;
34105491d2cSKalle Valo 		if (phy_rate > dma_rate) {
34205491d2cSKalle Valo 			tmp = ((fifo->ampdu_pld_size * phy_rate) /
34305491d2cSKalle Valo 			       ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
34405491d2cSKalle Valo 			tmp = min_t(u32, tmp, 255);
34505491d2cSKalle Valo 			fifo->mcs2ampdu_table[i] = (u8) tmp;
34605491d2cSKalle Valo 		}
34705491d2cSKalle Valo 	}
34805491d2cSKalle Valo }
34905491d2cSKalle Valo 
35005491d2cSKalle Valo /* evaluate the dma transfer rate using the tx underflows as feedback.
35105491d2cSKalle Valo  * If necessary, increase tx fifo preloading. If not enough,
35205491d2cSKalle Valo  * decrease maximum ampdu size for each mcs till underflows stop
35305491d2cSKalle Valo  * Return 1 if pre-loading not active, -1 if not an underflow event,
35405491d2cSKalle Valo  * 0 if pre-loading module took care of the event.
35505491d2cSKalle Valo  */
brcms_c_ffpld_check_txfunfl(struct brcms_c_info * wlc,int fid)35605491d2cSKalle Valo static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid)
35705491d2cSKalle Valo {
35805491d2cSKalle Valo 	struct ampdu_info *ampdu = wlc->ampdu;
35905491d2cSKalle Valo 	u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
36005491d2cSKalle Valo 	u32 txunfl_ratio;
36105491d2cSKalle Valo 	u8 max_mpdu;
36205491d2cSKalle Valo 	u32 current_ampdu_cnt = 0;
36305491d2cSKalle Valo 	u16 max_pld_size;
36405491d2cSKalle Valo 	u32 new_txunfl;
36505491d2cSKalle Valo 	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid);
36605491d2cSKalle Valo 	uint xmtfifo_sz;
36705491d2cSKalle Valo 	u16 cur_txunfl;
36805491d2cSKalle Valo 
36905491d2cSKalle Valo 	/* return if we got here for a different reason than underflows */
37005491d2cSKalle Valo 	cur_txunfl = brcms_b_read_shm(wlc->hw,
37105491d2cSKalle Valo 				      M_UCODE_MACSTAT +
37205491d2cSKalle Valo 				      offsetof(struct macstat, txfunfl[fid]));
37305491d2cSKalle Valo 	new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
37405491d2cSKalle Valo 	if (new_txunfl == 0) {
37505491d2cSKalle Valo 		brcms_dbg_ht(wlc->hw->d11core,
37605491d2cSKalle Valo 			     "TX status FRAG set but no tx underflows\n");
37705491d2cSKalle Valo 		return -1;
37805491d2cSKalle Valo 	}
37905491d2cSKalle Valo 	fifo->prev_txfunfl = cur_txunfl;
38005491d2cSKalle Valo 
38105491d2cSKalle Valo 	if (!ampdu->tx_max_funl)
38205491d2cSKalle Valo 		return 1;
38305491d2cSKalle Valo 
38405491d2cSKalle Valo 	/* check if fifo is big enough */
38505491d2cSKalle Valo 	if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz))
38605491d2cSKalle Valo 		return -1;
38705491d2cSKalle Valo 
38805491d2cSKalle Valo 	if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
38905491d2cSKalle Valo 		return 1;
39005491d2cSKalle Valo 
39105491d2cSKalle Valo 	max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
39205491d2cSKalle Valo 	fifo->accum_txfunfl += new_txunfl;
39305491d2cSKalle Valo 
39405491d2cSKalle Valo 	/* we need to wait for at least 10 underflows */
39505491d2cSKalle Valo 	if (fifo->accum_txfunfl < 10)
39605491d2cSKalle Valo 		return 0;
39705491d2cSKalle Valo 
39805491d2cSKalle Valo 	brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d  tx_underflows %d\n",
39905491d2cSKalle Valo 		     current_ampdu_cnt, fifo->accum_txfunfl);
40005491d2cSKalle Valo 
40105491d2cSKalle Valo 	/*
40205491d2cSKalle Valo 	   compute the current ratio of tx unfl per ampdu.
40305491d2cSKalle Valo 	   When the current ampdu count becomes too
40405491d2cSKalle Valo 	   big while the ratio remains small, we reset
40505491d2cSKalle Valo 	   the current count in order to not
40605491d2cSKalle Valo 	   introduce too big of a latency in detecting a
40705491d2cSKalle Valo 	   large amount of tx underflows later.
40805491d2cSKalle Valo 	 */
40905491d2cSKalle Valo 
41005491d2cSKalle Valo 	txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
41105491d2cSKalle Valo 
41205491d2cSKalle Valo 	if (txunfl_ratio > ampdu->tx_max_funl) {
41305491d2cSKalle Valo 		if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT)
41405491d2cSKalle Valo 			fifo->accum_txfunfl = 0;
41505491d2cSKalle Valo 
41605491d2cSKalle Valo 		return 0;
41705491d2cSKalle Valo 	}
41805491d2cSKalle Valo 	max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
41905491d2cSKalle Valo 			 AMPDU_NUM_MPDU_LEGACY);
42005491d2cSKalle Valo 
42105491d2cSKalle Valo 	/* In case max value max_pdu is already lower than
42205491d2cSKalle Valo 	   the fifo depth, there is nothing more we can do.
42305491d2cSKalle Valo 	 */
42405491d2cSKalle Valo 
42505491d2cSKalle Valo 	if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
42605491d2cSKalle Valo 		fifo->accum_txfunfl = 0;
42705491d2cSKalle Valo 		return 0;
42805491d2cSKalle Valo 	}
42905491d2cSKalle Valo 
43005491d2cSKalle Valo 	if (fifo->ampdu_pld_size < max_pld_size) {
43105491d2cSKalle Valo 
43205491d2cSKalle Valo 		/* increment by TX_FIFO_PLD_INC bytes */
43305491d2cSKalle Valo 		fifo->ampdu_pld_size += FFPLD_PLD_INCR;
43405491d2cSKalle Valo 		if (fifo->ampdu_pld_size > max_pld_size)
43505491d2cSKalle Valo 			fifo->ampdu_pld_size = max_pld_size;
43605491d2cSKalle Valo 
43705491d2cSKalle Valo 		/* update scb release size */
43805491d2cSKalle Valo 		brcms_c_scb_ampdu_update_config_all(ampdu);
43905491d2cSKalle Valo 
44005491d2cSKalle Valo 		/*
44105491d2cSKalle Valo 		 * compute a new dma xfer rate for max_mpdu @ max mcs.
44205491d2cSKalle Valo 		 * This is the minimum dma rate that can achieve no
44305491d2cSKalle Valo 		 * underflow condition for the current mpdu size.
44405491d2cSKalle Valo 		 *
44505491d2cSKalle Valo 		 * note : we divide/multiply by 100 to avoid integer overflows
44605491d2cSKalle Valo 		 */
44705491d2cSKalle Valo 		fifo->dmaxferrate =
44805491d2cSKalle Valo 		    (((phy_rate / 100) *
44905491d2cSKalle Valo 		      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
45005491d2cSKalle Valo 		     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
45105491d2cSKalle Valo 
45205491d2cSKalle Valo 		brcms_dbg_ht(wlc->hw->d11core,
45305491d2cSKalle Valo 			     "DMA estimated transfer rate %d; "
45405491d2cSKalle Valo 			     "pre-load size %d\n",
45505491d2cSKalle Valo 			     fifo->dmaxferrate, fifo->ampdu_pld_size);
45605491d2cSKalle Valo 	} else {
45705491d2cSKalle Valo 
45805491d2cSKalle Valo 		/* decrease ampdu size */
45905491d2cSKalle Valo 		if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
46005491d2cSKalle Valo 			if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
46105491d2cSKalle Valo 				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
46205491d2cSKalle Valo 				    AMPDU_NUM_MPDU_LEGACY - 1;
46305491d2cSKalle Valo 			else
46405491d2cSKalle Valo 				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
46505491d2cSKalle Valo 
46605491d2cSKalle Valo 			/* recompute the table */
46705491d2cSKalle Valo 			brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid);
46805491d2cSKalle Valo 
46905491d2cSKalle Valo 			/* update scb release size */
47005491d2cSKalle Valo 			brcms_c_scb_ampdu_update_config_all(ampdu);
47105491d2cSKalle Valo 		}
47205491d2cSKalle Valo 	}
47305491d2cSKalle Valo 	fifo->accum_txfunfl = 0;
47405491d2cSKalle Valo 	return 0;
47505491d2cSKalle Valo }
47605491d2cSKalle Valo 
47705491d2cSKalle Valo void
brcms_c_ampdu_tx_operational(struct brcms_c_info * wlc,u8 tid,uint max_rx_ampdu_bytes)47805491d2cSKalle Valo brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
47905491d2cSKalle Valo 	uint max_rx_ampdu_bytes) /* from ht_cap in beacon */
48005491d2cSKalle Valo {
48105491d2cSKalle Valo 	struct scb_ampdu *scb_ampdu;
48205491d2cSKalle Valo 	struct ampdu_info *ampdu = wlc->ampdu;
48305491d2cSKalle Valo 	struct scb *scb = &wlc->pri_scb;
48405491d2cSKalle Valo 	scb_ampdu = &scb->scb_ampdu;
48505491d2cSKalle Valo 
48605491d2cSKalle Valo 	if (!ampdu->ini_enable[tid]) {
48705491d2cSKalle Valo 		brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n",
48805491d2cSKalle Valo 			  __func__, tid);
48905491d2cSKalle Valo 		return;
49005491d2cSKalle Valo 	}
49105491d2cSKalle Valo 
49205491d2cSKalle Valo 	scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes;
49305491d2cSKalle Valo }
49405491d2cSKalle Valo 
brcms_c_ampdu_reset_session(struct brcms_ampdu_session * session,struct brcms_c_info * wlc)49505491d2cSKalle Valo void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
49605491d2cSKalle Valo 				 struct brcms_c_info *wlc)
49705491d2cSKalle Valo {
49805491d2cSKalle Valo 	session->wlc = wlc;
49905491d2cSKalle Valo 	skb_queue_head_init(&session->skb_list);
50005491d2cSKalle Valo 	session->max_ampdu_len = 0;    /* determined from first MPDU */
50105491d2cSKalle Valo 	session->max_ampdu_frames = 0; /* determined from first MPDU */
50205491d2cSKalle Valo 	session->ampdu_len = 0;
50305491d2cSKalle Valo 	session->dma_len = 0;
50405491d2cSKalle Valo }
50505491d2cSKalle Valo 
50605491d2cSKalle Valo /*
50705491d2cSKalle Valo  * Preps the given packet for AMPDU based on the session data. If the
50805491d2cSKalle Valo  * frame cannot be accomodated in the current session, -ENOSPC is
50905491d2cSKalle Valo  * returned.
51005491d2cSKalle Valo  */
brcms_c_ampdu_add_frame(struct brcms_ampdu_session * session,struct sk_buff * p)51105491d2cSKalle Valo int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
51205491d2cSKalle Valo 			    struct sk_buff *p)
51305491d2cSKalle Valo {
51405491d2cSKalle Valo 	struct brcms_c_info *wlc = session->wlc;
51505491d2cSKalle Valo 	struct ampdu_info *ampdu = wlc->ampdu;
51605491d2cSKalle Valo 	struct scb *scb = &wlc->pri_scb;
51705491d2cSKalle Valo 	struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
51805491d2cSKalle Valo 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
51905491d2cSKalle Valo 	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
52005491d2cSKalle Valo 	struct d11txh *txh = (struct d11txh *)p->data;
52105491d2cSKalle Valo 	unsigned ampdu_frames;
52205491d2cSKalle Valo 	u8 ndelim, tid;
52305491d2cSKalle Valo 	u8 *plcp;
52405491d2cSKalle Valo 	uint len;
52505491d2cSKalle Valo 	u16 mcl;
52605491d2cSKalle Valo 	bool fbr_iscck;
52705491d2cSKalle Valo 	bool rr;
52805491d2cSKalle Valo 
52905491d2cSKalle Valo 	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
53005491d2cSKalle Valo 	plcp = (u8 *)(txh + 1);
53105491d2cSKalle Valo 	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
53205491d2cSKalle Valo 	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
53305491d2cSKalle Valo 			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
53405491d2cSKalle Valo 	len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN;
53505491d2cSKalle Valo 
53605491d2cSKalle Valo 	ampdu_frames = skb_queue_len(&session->skb_list);
53705491d2cSKalle Valo 	if (ampdu_frames != 0) {
53805491d2cSKalle Valo 		struct sk_buff *first;
53905491d2cSKalle Valo 
54005491d2cSKalle Valo 		if (ampdu_frames + 1 > session->max_ampdu_frames ||
54105491d2cSKalle Valo 		    session->ampdu_len + len > session->max_ampdu_len)
54205491d2cSKalle Valo 			return -ENOSPC;
54305491d2cSKalle Valo 
54405491d2cSKalle Valo 		/*
54505491d2cSKalle Valo 		 * We aren't really out of space if the new frame is of
54605491d2cSKalle Valo 		 * a different priority, but we want the same behaviour
54705491d2cSKalle Valo 		 * so return -ENOSPC anyway.
54805491d2cSKalle Valo 		 *
54905491d2cSKalle Valo 		 * XXX: The old AMPDU code did this, but is it really
55005491d2cSKalle Valo 		 * necessary?
55105491d2cSKalle Valo 		 */
55205491d2cSKalle Valo 		first = skb_peek(&session->skb_list);
55305491d2cSKalle Valo 		if (p->priority != first->priority)
55405491d2cSKalle Valo 			return -ENOSPC;
55505491d2cSKalle Valo 	}
55605491d2cSKalle Valo 
55705491d2cSKalle Valo 	/*
55805491d2cSKalle Valo 	 * Now that we're sure this frame can be accomodated, update the
55905491d2cSKalle Valo 	 * session information.
56005491d2cSKalle Valo 	 */
56105491d2cSKalle Valo 	session->ampdu_len += len;
56205491d2cSKalle Valo 	session->dma_len += p->len;
56305491d2cSKalle Valo 
56405491d2cSKalle Valo 	tid = (u8)p->priority;
56505491d2cSKalle Valo 
56605491d2cSKalle Valo 	/* Handle retry limits */
56705491d2cSKalle Valo 	if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) {
56805491d2cSKalle Valo 		txrate[0].count++;
56905491d2cSKalle Valo 		rr = true;
57005491d2cSKalle Valo 	} else {
57105491d2cSKalle Valo 		txrate[1].count++;
57205491d2cSKalle Valo 		rr = false;
57305491d2cSKalle Valo 	}
57405491d2cSKalle Valo 
57505491d2cSKalle Valo 	if (ampdu_frames == 0) {
57605491d2cSKalle Valo 		u8 plcp0, plcp3, is40, sgi, mcs;
57705491d2cSKalle Valo 		uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
57805491d2cSKalle Valo 		struct brcms_fifo_info *f = &ampdu->fifo_tb[fifo];
57905491d2cSKalle Valo 
58005491d2cSKalle Valo 		if (rr) {
58105491d2cSKalle Valo 			plcp0 = plcp[0];
58205491d2cSKalle Valo 			plcp3 = plcp[3];
58305491d2cSKalle Valo 		} else {
58405491d2cSKalle Valo 			plcp0 = txh->FragPLCPFallback[0];
58505491d2cSKalle Valo 			plcp3 = txh->FragPLCPFallback[3];
58605491d2cSKalle Valo 
58705491d2cSKalle Valo 		}
58805491d2cSKalle Valo 
58905491d2cSKalle Valo 		/* Limit AMPDU size based on MCS */
59005491d2cSKalle Valo 		is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
59105491d2cSKalle Valo 		sgi = plcp3_issgi(plcp3) ? 1 : 0;
59205491d2cSKalle Valo 		mcs = plcp0 & ~MIMO_PLCP_40MHZ;
59305491d2cSKalle Valo 		session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes,
59405491d2cSKalle Valo 					     ampdu->max_txlen[mcs][is40][sgi]);
59505491d2cSKalle Valo 
59605491d2cSKalle Valo 		session->max_ampdu_frames = scb_ampdu->max_pdu;
59705491d2cSKalle Valo 		if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
59805491d2cSKalle Valo 			session->max_ampdu_frames =
59905491d2cSKalle Valo 				min_t(u16, f->mcs2ampdu_table[mcs],
60005491d2cSKalle Valo 				      session->max_ampdu_frames);
60105491d2cSKalle Valo 		}
60205491d2cSKalle Valo 	}
60305491d2cSKalle Valo 
60405491d2cSKalle Valo 	/*
60505491d2cSKalle Valo 	 * Treat all frames as "middle" frames of AMPDU here. First and
60605491d2cSKalle Valo 	 * last frames must be fixed up after all MPDUs have been prepped.
60705491d2cSKalle Valo 	 */
60805491d2cSKalle Valo 	mcl = le16_to_cpu(txh->MacTxControlLow);
60905491d2cSKalle Valo 	mcl &= ~TXC_AMPDU_MASK;
61005491d2cSKalle Valo 	mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
61105491d2cSKalle Valo 	mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
61205491d2cSKalle Valo 	txh->MacTxControlLow = cpu_to_le16(mcl);
61305491d2cSKalle Valo 	txh->PreloadSize = 0;	/* always default to 0 */
61405491d2cSKalle Valo 
61505491d2cSKalle Valo 	skb_queue_tail(&session->skb_list, p);
61605491d2cSKalle Valo 
61705491d2cSKalle Valo 	return 0;
61805491d2cSKalle Valo }
61905491d2cSKalle Valo 
brcms_c_ampdu_finalize(struct brcms_ampdu_session * session)62005491d2cSKalle Valo void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session)
62105491d2cSKalle Valo {
62205491d2cSKalle Valo 	struct brcms_c_info *wlc = session->wlc;
62305491d2cSKalle Valo 	struct ampdu_info *ampdu = wlc->ampdu;
62405491d2cSKalle Valo 	struct sk_buff *first, *last;
62505491d2cSKalle Valo 	struct d11txh *txh;
62605491d2cSKalle Valo 	struct ieee80211_tx_info *tx_info;
62705491d2cSKalle Valo 	struct ieee80211_tx_rate *txrate;
62805491d2cSKalle Valo 	u8 ndelim;
62905491d2cSKalle Valo 	u8 *plcp;
63005491d2cSKalle Valo 	uint len;
63105491d2cSKalle Valo 	uint fifo;
63205491d2cSKalle Valo 	struct brcms_fifo_info *f;
63305491d2cSKalle Valo 	u16 mcl;
63405491d2cSKalle Valo 	bool fbr;
63505491d2cSKalle Valo 	bool fbr_iscck;
63605491d2cSKalle Valo 	struct ieee80211_rts *rts;
63705491d2cSKalle Valo 	bool use_rts = false, use_cts = false;
63805491d2cSKalle Valo 	u16 dma_len = session->dma_len;
63905491d2cSKalle Valo 	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
64005491d2cSKalle Valo 	u32 rspec = 0, rspec_fallback = 0;
64105491d2cSKalle Valo 	u32 rts_rspec = 0, rts_rspec_fallback = 0;
642d7f95d92SLee Jones 	u8 plcp0, is40, mcs;
64305491d2cSKalle Valo 	u16 mch;
64405491d2cSKalle Valo 	u8 preamble_type = BRCMS_GF_PREAMBLE;
64505491d2cSKalle Valo 	u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
64605491d2cSKalle Valo 	u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
64705491d2cSKalle Valo 	u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;
64805491d2cSKalle Valo 
64905491d2cSKalle Valo 	if (skb_queue_empty(&session->skb_list))
65005491d2cSKalle Valo 		return;
65105491d2cSKalle Valo 
65205491d2cSKalle Valo 	first = skb_peek(&session->skb_list);
65305491d2cSKalle Valo 	last = skb_peek_tail(&session->skb_list);
65405491d2cSKalle Valo 
65505491d2cSKalle Valo 	/* Need to fix up last MPDU first to adjust AMPDU length */
65605491d2cSKalle Valo 	txh = (struct d11txh *)last->data;
65705491d2cSKalle Valo 	fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
65805491d2cSKalle Valo 	f = &ampdu->fifo_tb[fifo];
65905491d2cSKalle Valo 
66005491d2cSKalle Valo 	mcl = le16_to_cpu(txh->MacTxControlLow);
66105491d2cSKalle Valo 	mcl &= ~TXC_AMPDU_MASK;
66205491d2cSKalle Valo 	mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
66305491d2cSKalle Valo 	txh->MacTxControlLow = cpu_to_le16(mcl);
66405491d2cSKalle Valo 
66505491d2cSKalle Valo 	/* remove the null delimiter after last mpdu */
66605491d2cSKalle Valo 	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
66705491d2cSKalle Valo 	txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
66805491d2cSKalle Valo 	session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
66905491d2cSKalle Valo 
67005491d2cSKalle Valo 	/* remove the pad len from last mpdu */
67105491d2cSKalle Valo 	fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
67205491d2cSKalle Valo 	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
67305491d2cSKalle Valo 			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
67405491d2cSKalle Valo 	session->ampdu_len -= roundup(len, 4) - len;
67505491d2cSKalle Valo 
67605491d2cSKalle Valo 	/* Now fix up the first MPDU */
67705491d2cSKalle Valo 	tx_info = IEEE80211_SKB_CB(first);
67805491d2cSKalle Valo 	txrate = tx_info->status.rates;
67905491d2cSKalle Valo 	txh = (struct d11txh *)first->data;
68005491d2cSKalle Valo 	plcp = (u8 *)(txh + 1);
68105491d2cSKalle Valo 	rts = (struct ieee80211_rts *)&txh->rts_frame;
68205491d2cSKalle Valo 
68305491d2cSKalle Valo 	mcl = le16_to_cpu(txh->MacTxControlLow);
68405491d2cSKalle Valo 	/* If only one MPDU leave it marked as last */
68505491d2cSKalle Valo 	if (first != last) {
68605491d2cSKalle Valo 		mcl &= ~TXC_AMPDU_MASK;
68705491d2cSKalle Valo 		mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
68805491d2cSKalle Valo 	}
68905491d2cSKalle Valo 	mcl |= TXC_STARTMSDU;
69005491d2cSKalle Valo 	if (ieee80211_is_rts(rts->frame_control)) {
69105491d2cSKalle Valo 		mcl |= TXC_SENDRTS;
69205491d2cSKalle Valo 		use_rts = true;
69305491d2cSKalle Valo 	}
69405491d2cSKalle Valo 	if (ieee80211_is_cts(rts->frame_control)) {
69505491d2cSKalle Valo 		mcl |= TXC_SENDCTS;
69605491d2cSKalle Valo 		use_cts = true;
69705491d2cSKalle Valo 	}
69805491d2cSKalle Valo 	txh->MacTxControlLow = cpu_to_le16(mcl);
69905491d2cSKalle Valo 
70005491d2cSKalle Valo 	fbr = txrate[1].count > 0;
701d7f95d92SLee Jones 	if (!fbr)
70205491d2cSKalle Valo 		plcp0 = plcp[0];
703d7f95d92SLee Jones 	else
70405491d2cSKalle Valo 		plcp0 = txh->FragPLCPFallback[0];
705d7f95d92SLee Jones 
70605491d2cSKalle Valo 	is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
70705491d2cSKalle Valo 	mcs = plcp0 & ~MIMO_PLCP_40MHZ;
70805491d2cSKalle Valo 
70905491d2cSKalle Valo 	if (is40) {
71005491d2cSKalle Valo 		if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi)))
71105491d2cSKalle Valo 			mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP;
71205491d2cSKalle Valo 		else
71305491d2cSKalle Valo 			mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
71405491d2cSKalle Valo 	}
71505491d2cSKalle Valo 
71605491d2cSKalle Valo 	/* rebuild the rspec and rspec_fallback */
71705491d2cSKalle Valo 	rspec = RSPEC_MIMORATE;
71805491d2cSKalle Valo 	rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
71905491d2cSKalle Valo 	if (plcp[0] & MIMO_PLCP_40MHZ)
72005491d2cSKalle Valo 		rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
72105491d2cSKalle Valo 
72205491d2cSKalle Valo 	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
72305491d2cSKalle Valo 	if (fbr_iscck) {
72405491d2cSKalle Valo 		rspec_fallback =
72505491d2cSKalle Valo 			cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0]));
72605491d2cSKalle Valo 	} else {
72705491d2cSKalle Valo 		rspec_fallback = RSPEC_MIMORATE;
72805491d2cSKalle Valo 		rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
72905491d2cSKalle Valo 		if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
73005491d2cSKalle Valo 			rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT;
73105491d2cSKalle Valo 	}
73205491d2cSKalle Valo 
73305491d2cSKalle Valo 	if (use_rts || use_cts) {
73405491d2cSKalle Valo 		rts_rspec =
73505491d2cSKalle Valo 			brcms_c_rspec_to_rts_rspec(wlc, rspec,
73605491d2cSKalle Valo 						   false, mimo_ctlchbw);
73705491d2cSKalle Valo 		rts_rspec_fallback =
73805491d2cSKalle Valo 			brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback,
73905491d2cSKalle Valo 						   false, mimo_ctlchbw);
74005491d2cSKalle Valo 	}
74105491d2cSKalle Valo 
74205491d2cSKalle Valo 	BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len);
74305491d2cSKalle Valo 	/* mark plcp to indicate ampdu */
74405491d2cSKalle Valo 	BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
74505491d2cSKalle Valo 
74605491d2cSKalle Valo 	/* reset the mixed mode header durations */
74705491d2cSKalle Valo 	if (txh->MModeLen) {
74805491d2cSKalle Valo 		u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec,
74905491d2cSKalle Valo 						     session->ampdu_len);
75005491d2cSKalle Valo 		txh->MModeLen = cpu_to_le16(mmodelen);
75105491d2cSKalle Valo 		preamble_type = BRCMS_MM_PREAMBLE;
75205491d2cSKalle Valo 	}
75305491d2cSKalle Valo 	if (txh->MModeFbrLen) {
75405491d2cSKalle Valo 		u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback,
75505491d2cSKalle Valo 						     session->ampdu_len);
75605491d2cSKalle Valo 		txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
75705491d2cSKalle Valo 		fbr_preamble_type = BRCMS_MM_PREAMBLE;
75805491d2cSKalle Valo 	}
75905491d2cSKalle Valo 
76005491d2cSKalle Valo 	/* set the preload length */
76105491d2cSKalle Valo 	if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
76205491d2cSKalle Valo 		dma_len = min(dma_len, f->ampdu_pld_size);
76305491d2cSKalle Valo 		txh->PreloadSize = cpu_to_le16(dma_len);
76405491d2cSKalle Valo 	} else {
76505491d2cSKalle Valo 		txh->PreloadSize = 0;
76605491d2cSKalle Valo 	}
76705491d2cSKalle Valo 
76805491d2cSKalle Valo 	mch = le16_to_cpu(txh->MacTxControlHigh);
76905491d2cSKalle Valo 
77005491d2cSKalle Valo 	/* update RTS dur fields */
77105491d2cSKalle Valo 	if (use_rts || use_cts) {
77205491d2cSKalle Valo 		u16 durid;
77305491d2cSKalle Valo 		if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
77405491d2cSKalle Valo 		    TXC_PREAMBLE_RTS_MAIN_SHORT)
77505491d2cSKalle Valo 			rts_preamble_type = BRCMS_SHORT_PREAMBLE;
77605491d2cSKalle Valo 
77705491d2cSKalle Valo 		if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
77805491d2cSKalle Valo 		     TXC_PREAMBLE_RTS_FB_SHORT)
77905491d2cSKalle Valo 			rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE;
78005491d2cSKalle Valo 
78105491d2cSKalle Valo 		durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
78205491d2cSKalle Valo 						   rspec, rts_preamble_type,
78305491d2cSKalle Valo 						   preamble_type,
78405491d2cSKalle Valo 						   session->ampdu_len, true);
78505491d2cSKalle Valo 		rts->duration = cpu_to_le16(durid);
78605491d2cSKalle Valo 		durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
78705491d2cSKalle Valo 						   rts_rspec_fallback,
78805491d2cSKalle Valo 						   rspec_fallback,
78905491d2cSKalle Valo 						   rts_fbr_preamble_type,
79005491d2cSKalle Valo 						   fbr_preamble_type,
79105491d2cSKalle Valo 						   session->ampdu_len, true);
79205491d2cSKalle Valo 		txh->RTSDurFallback = cpu_to_le16(durid);
79305491d2cSKalle Valo 		/* set TxFesTimeNormal */
79405491d2cSKalle Valo 		txh->TxFesTimeNormal = rts->duration;
79505491d2cSKalle Valo 		/* set fallback rate version of TxFesTimeNormal */
79605491d2cSKalle Valo 		txh->TxFesTimeFallback = txh->RTSDurFallback;
79705491d2cSKalle Valo 	}
79805491d2cSKalle Valo 
79905491d2cSKalle Valo 	/* set flag and plcp for fallback rate */
80005491d2cSKalle Valo 	if (fbr) {
80105491d2cSKalle Valo 		mch |= TXC_AMPDU_FBR;
80205491d2cSKalle Valo 		txh->MacTxControlHigh = cpu_to_le16(mch);
80305491d2cSKalle Valo 		BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
80405491d2cSKalle Valo 		BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
80505491d2cSKalle Valo 	}
80605491d2cSKalle Valo 
80705491d2cSKalle Valo 	brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n",
80805491d2cSKalle Valo 		     wlc->pub->unit, skb_queue_len(&session->skb_list),
80905491d2cSKalle Valo 		     session->ampdu_len);
81005491d2cSKalle Valo }
81105491d2cSKalle Valo 
81205491d2cSKalle Valo static void
brcms_c_ampdu_rate_status(struct brcms_c_info * wlc,struct ieee80211_tx_info * tx_info,struct tx_status * txs,u8 mcs)81305491d2cSKalle Valo brcms_c_ampdu_rate_status(struct brcms_c_info *wlc,
81405491d2cSKalle Valo 			  struct ieee80211_tx_info *tx_info,
81505491d2cSKalle Valo 			  struct tx_status *txs, u8 mcs)
81605491d2cSKalle Valo {
81705491d2cSKalle Valo 	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
81805491d2cSKalle Valo 	int i;
81905491d2cSKalle Valo 
82005491d2cSKalle Valo 	/* clear the rest of the rates */
82105491d2cSKalle Valo 	for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
82205491d2cSKalle Valo 		txrate[i].idx = -1;
82305491d2cSKalle Valo 		txrate[i].count = 0;
82405491d2cSKalle Valo 	}
82505491d2cSKalle Valo }
82605491d2cSKalle Valo 
82705491d2cSKalle Valo static void
brcms_c_ampdu_dotxstatus_complete(struct ampdu_info * ampdu,struct scb * scb,struct sk_buff * p,struct tx_status * txs,u32 s1,u32 s2)82805491d2cSKalle Valo brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
82905491d2cSKalle Valo 			      struct sk_buff *p, struct tx_status *txs,
83005491d2cSKalle Valo 			      u32 s1, u32 s2)
83105491d2cSKalle Valo {
83205491d2cSKalle Valo 	struct scb_ampdu *scb_ampdu;
83305491d2cSKalle Valo 	struct brcms_c_info *wlc = ampdu->wlc;
83405491d2cSKalle Valo 	struct scb_ampdu_tid_ini *ini;
83505491d2cSKalle Valo 	u8 bitmap[8], queue, tid;
83605491d2cSKalle Valo 	struct d11txh *txh;
83705491d2cSKalle Valo 	u8 *plcp;
83805491d2cSKalle Valo 	struct ieee80211_hdr *h;
83905491d2cSKalle Valo 	u16 seq, start_seq = 0, bindex, index, mcl;
84005491d2cSKalle Valo 	u8 mcs = 0;
84105491d2cSKalle Valo 	bool ba_recd = false, ack_recd = false;
842*2f73f04bSTom Rix 	u8 tot_mpdu = 0;
84305491d2cSKalle Valo 	uint supr_status;
844f5c3bf15SLee Jones 	bool retry = true;
84505491d2cSKalle Valo 	u16 mimoantsel = 0;
846d7f95d92SLee Jones 	u8 retry_limit;
84705491d2cSKalle Valo 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
84805491d2cSKalle Valo 
84905491d2cSKalle Valo #ifdef DEBUG
85005491d2cSKalle Valo 	u8 hole[AMPDU_MAX_MPDU];
85105491d2cSKalle Valo 	memset(hole, 0, sizeof(hole));
85205491d2cSKalle Valo #endif
85305491d2cSKalle Valo 
85405491d2cSKalle Valo 	scb_ampdu = &scb->scb_ampdu;
85505491d2cSKalle Valo 	tid = (u8) (p->priority);
85605491d2cSKalle Valo 
85705491d2cSKalle Valo 	ini = &scb_ampdu->ini[tid];
85805491d2cSKalle Valo 	retry_limit = ampdu->retry_limit_tid[tid];
85905491d2cSKalle Valo 	memset(bitmap, 0, sizeof(bitmap));
86005491d2cSKalle Valo 	queue = txs->frameid & TXFID_QUEUE_MASK;
86105491d2cSKalle Valo 	supr_status = txs->status & TX_STATUS_SUPR_MASK;
86205491d2cSKalle Valo 
86305491d2cSKalle Valo 	if (txs->status & TX_STATUS_ACK_RCV) {
86405491d2cSKalle Valo 		WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE));
86505491d2cSKalle Valo 		start_seq = txs->sequence >> SEQNUM_SHIFT;
86605491d2cSKalle Valo 		bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
86705491d2cSKalle Valo 		    TX_STATUS_BA_BMAP03_SHIFT;
86805491d2cSKalle Valo 
86905491d2cSKalle Valo 		WARN_ON(s1 & TX_STATUS_INTERMEDIATE);
87005491d2cSKalle Valo 		WARN_ON(!(s1 & TX_STATUS_AMPDU));
87105491d2cSKalle Valo 
87205491d2cSKalle Valo 		bitmap[0] |=
87305491d2cSKalle Valo 		    (s1 & TX_STATUS_BA_BMAP47_MASK) <<
87405491d2cSKalle Valo 		    TX_STATUS_BA_BMAP47_SHIFT;
87505491d2cSKalle Valo 		bitmap[1] = (s1 >> 8) & 0xff;
87605491d2cSKalle Valo 		bitmap[2] = (s1 >> 16) & 0xff;
87705491d2cSKalle Valo 		bitmap[3] = (s1 >> 24) & 0xff;
87805491d2cSKalle Valo 
87905491d2cSKalle Valo 		bitmap[4] = s2 & 0xff;
88005491d2cSKalle Valo 		bitmap[5] = (s2 >> 8) & 0xff;
88105491d2cSKalle Valo 		bitmap[6] = (s2 >> 16) & 0xff;
88205491d2cSKalle Valo 		bitmap[7] = (s2 >> 24) & 0xff;
88305491d2cSKalle Valo 
88405491d2cSKalle Valo 		ba_recd = true;
88505491d2cSKalle Valo 	} else {
88605491d2cSKalle Valo 		if (supr_status) {
88705491d2cSKalle Valo 			if (supr_status == TX_STATUS_SUPR_BADCH) {
88805491d2cSKalle Valo 				brcms_dbg_ht(wlc->hw->d11core,
88905491d2cSKalle Valo 					  "%s: Pkt tx suppressed, illegal channel possibly %d\n",
89005491d2cSKalle Valo 					  __func__, CHSPEC_CHANNEL(
89105491d2cSKalle Valo 					  wlc->default_bss->chanspec));
89205491d2cSKalle Valo 			} else {
89305491d2cSKalle Valo 				if (supr_status != TX_STATUS_SUPR_FRAG)
89405491d2cSKalle Valo 					brcms_err(wlc->hw->d11core,
89505491d2cSKalle Valo 						  "%s: supr_status 0x%x\n",
89605491d2cSKalle Valo 						  __func__, supr_status);
89705491d2cSKalle Valo 			}
89805491d2cSKalle Valo 			/* no need to retry for badch; will fail again */
89905491d2cSKalle Valo 			if (supr_status == TX_STATUS_SUPR_BADCH ||
90005491d2cSKalle Valo 			    supr_status == TX_STATUS_SUPR_EXPTIME) {
90105491d2cSKalle Valo 				retry = false;
90205491d2cSKalle Valo 			} else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
90305491d2cSKalle Valo 				/* TX underflow:
90405491d2cSKalle Valo 				 *   try tuning pre-loading or ampdu size
90505491d2cSKalle Valo 				 */
90605491d2cSKalle Valo 			} else if (supr_status == TX_STATUS_SUPR_FRAG) {
90705491d2cSKalle Valo 				/*
90805491d2cSKalle Valo 				 * if there were underflows, but pre-loading
90905491d2cSKalle Valo 				 * is not active, notify rate adaptation.
91005491d2cSKalle Valo 				 */
911d7f95d92SLee Jones 				brcms_c_ffpld_check_txfunfl(wlc, queue);
91205491d2cSKalle Valo 			}
91305491d2cSKalle Valo 		} else if (txs->phyerr) {
91405491d2cSKalle Valo 			brcms_dbg_ht(wlc->hw->d11core,
91505491d2cSKalle Valo 				     "%s: ampdu tx phy error (0x%x)\n",
91605491d2cSKalle Valo 				     __func__, txs->phyerr);
91705491d2cSKalle Valo 		}
91805491d2cSKalle Valo 	}
91905491d2cSKalle Valo 
92005491d2cSKalle Valo 	/* loop through all pkts and retry if not acked */
92105491d2cSKalle Valo 	while (p) {
92205491d2cSKalle Valo 		tx_info = IEEE80211_SKB_CB(p);
92305491d2cSKalle Valo 		txh = (struct d11txh *) p->data;
92405491d2cSKalle Valo 		mcl = le16_to_cpu(txh->MacTxControlLow);
92505491d2cSKalle Valo 		plcp = (u8 *) (txh + 1);
92605491d2cSKalle Valo 		h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
92705491d2cSKalle Valo 		seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
92805491d2cSKalle Valo 
92905491d2cSKalle Valo 		trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh));
93005491d2cSKalle Valo 
93105491d2cSKalle Valo 		if (tot_mpdu == 0) {
93205491d2cSKalle Valo 			mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
93305491d2cSKalle Valo 			mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel);
93405491d2cSKalle Valo 		}
93505491d2cSKalle Valo 
93605491d2cSKalle Valo 		index = TX_SEQ_TO_INDEX(seq);
93705491d2cSKalle Valo 		ack_recd = false;
93805491d2cSKalle Valo 		if (ba_recd) {
93901c195deSDmitry Safonov 			int block_acked;
94001c195deSDmitry Safonov 
94105491d2cSKalle Valo 			bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
94201c195deSDmitry Safonov 			if (bindex < AMPDU_TX_BA_MAX_WSIZE)
94301c195deSDmitry Safonov 				block_acked = isset(bitmap, bindex);
94401c195deSDmitry Safonov 			else
94501c195deSDmitry Safonov 				block_acked = 0;
94605491d2cSKalle Valo 			brcms_dbg_ht(wlc->hw->d11core,
94705491d2cSKalle Valo 				     "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n",
94805491d2cSKalle Valo 				     tid, seq, start_seq, bindex,
94901c195deSDmitry Safonov 				     block_acked, index);
95005491d2cSKalle Valo 			/* if acked then clear bit and free packet */
95101c195deSDmitry Safonov 			if (block_acked) {
95205491d2cSKalle Valo 				ini->txretry[index] = 0;
95305491d2cSKalle Valo 
95405491d2cSKalle Valo 				/*
95505491d2cSKalle Valo 				 * ampdu_ack_len:
95605491d2cSKalle Valo 				 *   number of acked aggregated frames
95705491d2cSKalle Valo 				 */
95805491d2cSKalle Valo 				/* ampdu_len: number of aggregated frames */
95905491d2cSKalle Valo 				brcms_c_ampdu_rate_status(wlc, tx_info, txs,
96005491d2cSKalle Valo 							  mcs);
96105491d2cSKalle Valo 				tx_info->flags |= IEEE80211_TX_STAT_ACK;
96205491d2cSKalle Valo 				tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
96305491d2cSKalle Valo 				tx_info->status.ampdu_ack_len =
96405491d2cSKalle Valo 					tx_info->status.ampdu_len = 1;
96505491d2cSKalle Valo 
96605491d2cSKalle Valo 				skb_pull(p, D11_PHY_HDR_LEN);
96705491d2cSKalle Valo 				skb_pull(p, D11_TXH_LEN);
96805491d2cSKalle Valo 
96905491d2cSKalle Valo 				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
97005491d2cSKalle Valo 							    p);
97105491d2cSKalle Valo 				ack_recd = true;
97205491d2cSKalle Valo 			}
97305491d2cSKalle Valo 		}
97405491d2cSKalle Valo 		/* either retransmit or send bar if ack not recd */
97505491d2cSKalle Valo 		if (!ack_recd) {
97605491d2cSKalle Valo 			if (retry && (ini->txretry[index] < (int)retry_limit)) {
97705491d2cSKalle Valo 				int ret;
97805491d2cSKalle Valo 				ini->txretry[index]++;
97905491d2cSKalle Valo 				ret = brcms_c_txfifo(wlc, queue, p);
98005491d2cSKalle Valo 				/*
98105491d2cSKalle Valo 				 * We shouldn't be out of space in the DMA
98205491d2cSKalle Valo 				 * ring here since we're reinserting a frame
98305491d2cSKalle Valo 				 * that was just pulled out.
98405491d2cSKalle Valo 				 */
98505491d2cSKalle Valo 				WARN_ONCE(ret, "queue %d out of txds\n", queue);
98605491d2cSKalle Valo 			} else {
98705491d2cSKalle Valo 				/* Retry timeout */
98805491d2cSKalle Valo 				ieee80211_tx_info_clear_status(tx_info);
98905491d2cSKalle Valo 				tx_info->status.ampdu_ack_len = 0;
99005491d2cSKalle Valo 				tx_info->status.ampdu_len = 1;
99105491d2cSKalle Valo 				tx_info->flags |=
99205491d2cSKalle Valo 				    IEEE80211_TX_STAT_AMPDU_NO_BACK;
99305491d2cSKalle Valo 				skb_pull(p, D11_PHY_HDR_LEN);
99405491d2cSKalle Valo 				skb_pull(p, D11_TXH_LEN);
99505491d2cSKalle Valo 				brcms_dbg_ht(wlc->hw->d11core,
99605491d2cSKalle Valo 					     "BA Timeout, seq %d\n",
99705491d2cSKalle Valo 					     seq);
99805491d2cSKalle Valo 				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
99905491d2cSKalle Valo 							    p);
100005491d2cSKalle Valo 			}
100105491d2cSKalle Valo 		}
100205491d2cSKalle Valo 		tot_mpdu++;
100305491d2cSKalle Valo 
100405491d2cSKalle Valo 		/* break out if last packet of ampdu */
100505491d2cSKalle Valo 		if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
100605491d2cSKalle Valo 		    TXC_AMPDU_LAST)
100705491d2cSKalle Valo 			break;
100805491d2cSKalle Valo 
100905491d2cSKalle Valo 		p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
101005491d2cSKalle Valo 	}
101105491d2cSKalle Valo 
101205491d2cSKalle Valo 	/* update rate state */
1013d7f95d92SLee Jones 	brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
101405491d2cSKalle Valo }
101505491d2cSKalle Valo 
101605491d2cSKalle Valo void
brcms_c_ampdu_dotxstatus(struct ampdu_info * ampdu,struct scb * scb,struct sk_buff * p,struct tx_status * txs)101705491d2cSKalle Valo brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
101805491d2cSKalle Valo 		     struct sk_buff *p, struct tx_status *txs)
101905491d2cSKalle Valo {
102005491d2cSKalle Valo 	struct brcms_c_info *wlc = ampdu->wlc;
102105491d2cSKalle Valo 	u32 s1 = 0, s2 = 0;
102205491d2cSKalle Valo 
102305491d2cSKalle Valo 	/* BMAC_NOTE: For the split driver, second level txstatus comes later
102405491d2cSKalle Valo 	 * So if the ACK was received then wait for the second level else just
102505491d2cSKalle Valo 	 * call the first one
102605491d2cSKalle Valo 	 */
102705491d2cSKalle Valo 	if (txs->status & TX_STATUS_ACK_RCV) {
102805491d2cSKalle Valo 		u8 status_delay = 0;
102905491d2cSKalle Valo 
103005491d2cSKalle Valo 		/* wait till the next 8 bytes of txstatus is available */
103105491d2cSKalle Valo 		s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus));
103205491d2cSKalle Valo 		while ((s1 & TXS_V) == 0) {
103305491d2cSKalle Valo 			udelay(1);
103405491d2cSKalle Valo 			status_delay++;
103505491d2cSKalle Valo 			if (status_delay > 10)
103605491d2cSKalle Valo 				return; /* error condition */
103705491d2cSKalle Valo 			s1 = bcma_read32(wlc->hw->d11core,
103805491d2cSKalle Valo 					 D11REGOFFS(frmtxstatus));
103905491d2cSKalle Valo 		}
104005491d2cSKalle Valo 
104105491d2cSKalle Valo 		s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2));
104205491d2cSKalle Valo 	}
104305491d2cSKalle Valo 
104405491d2cSKalle Valo 	if (scb) {
104505491d2cSKalle Valo 		brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
104605491d2cSKalle Valo 	} else {
104705491d2cSKalle Valo 		/* loop through all pkts and free */
104805491d2cSKalle Valo 		u8 queue = txs->frameid & TXFID_QUEUE_MASK;
104905491d2cSKalle Valo 		struct d11txh *txh;
105005491d2cSKalle Valo 		u16 mcl;
105105491d2cSKalle Valo 		while (p) {
105205491d2cSKalle Valo 			txh = (struct d11txh *) p->data;
105305491d2cSKalle Valo 			trace_brcms_txdesc(&wlc->hw->d11core->dev, txh,
105405491d2cSKalle Valo 					   sizeof(*txh));
105505491d2cSKalle Valo 			mcl = le16_to_cpu(txh->MacTxControlLow);
105605491d2cSKalle Valo 			brcmu_pkt_buf_free_skb(p);
105705491d2cSKalle Valo 			/* break out if last packet of ampdu */
105805491d2cSKalle Valo 			if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
105905491d2cSKalle Valo 			    TXC_AMPDU_LAST)
106005491d2cSKalle Valo 				break;
106105491d2cSKalle Valo 			p = dma_getnexttxp(wlc->hw->di[queue],
106205491d2cSKalle Valo 					   DMA_RANGE_TRANSMITTED);
106305491d2cSKalle Valo 		}
106405491d2cSKalle Valo 	}
106505491d2cSKalle Valo }
106605491d2cSKalle Valo 
brcms_c_ampdu_macaddr_upd(struct brcms_c_info * wlc)106705491d2cSKalle Valo void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc)
106805491d2cSKalle Valo {
106905491d2cSKalle Valo 	char template[T_RAM_ACCESS_SZ * 2];
107005491d2cSKalle Valo 
107105491d2cSKalle Valo 	/* driver needs to write the ta in the template; ta is at offset 16 */
107205491d2cSKalle Valo 	memset(template, 0, sizeof(template));
107305491d2cSKalle Valo 	memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
107405491d2cSKalle Valo 	brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16),
107505491d2cSKalle Valo 				  (T_RAM_ACCESS_SZ * 2),
107605491d2cSKalle Valo 				  template);
107705491d2cSKalle Valo }
107805491d2cSKalle Valo 
brcms_c_aggregatable(struct brcms_c_info * wlc,u8 tid)107905491d2cSKalle Valo bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid)
108005491d2cSKalle Valo {
108105491d2cSKalle Valo 	return wlc->ampdu->ini_enable[tid];
108205491d2cSKalle Valo }
108305491d2cSKalle Valo 
brcms_c_ampdu_shm_upd(struct ampdu_info * ampdu)108405491d2cSKalle Valo void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu)
108505491d2cSKalle Valo {
108605491d2cSKalle Valo 	struct brcms_c_info *wlc = ampdu->wlc;
108705491d2cSKalle Valo 
108805491d2cSKalle Valo 	/*
108905491d2cSKalle Valo 	 * Extend ucode internal watchdog timer to
109005491d2cSKalle Valo 	 * match larger received frames
109105491d2cSKalle Valo 	 */
109205491d2cSKalle Valo 	if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
109305491d2cSKalle Valo 	    IEEE80211_HT_MAX_AMPDU_64K) {
109405491d2cSKalle Valo 		brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
109505491d2cSKalle Valo 		brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
109605491d2cSKalle Valo 	} else {
109705491d2cSKalle Valo 		brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
109805491d2cSKalle Valo 		brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
109905491d2cSKalle Valo 	}
110005491d2cSKalle Valo }
110105491d2cSKalle Valo 
110205491d2cSKalle Valo /*
110305491d2cSKalle Valo  * callback function that helps invalidating ampdu packets in a DMA queue
110405491d2cSKalle Valo  */
dma_cb_fn_ampdu(void * txi,void * arg_a)110505491d2cSKalle Valo static void dma_cb_fn_ampdu(void *txi, void *arg_a)
110605491d2cSKalle Valo {
110705491d2cSKalle Valo 	struct ieee80211_sta *sta = arg_a;
110805491d2cSKalle Valo 	struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi;
110905491d2cSKalle Valo 
111005491d2cSKalle Valo 	if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
111105491d2cSKalle Valo 	    (tx_info->rate_driver_data[0] == sta || sta == NULL))
111205491d2cSKalle Valo 		tx_info->rate_driver_data[0] = NULL;
111305491d2cSKalle Valo }
111405491d2cSKalle Valo 
111505491d2cSKalle Valo /*
111605491d2cSKalle Valo  * When a remote party is no longer available for ampdu communication, any
111705491d2cSKalle Valo  * pending tx ampdu packets in the driver have to be flushed.
111805491d2cSKalle Valo  */
brcms_c_ampdu_flush(struct brcms_c_info * wlc,struct ieee80211_sta * sta,u16 tid)111905491d2cSKalle Valo void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
112005491d2cSKalle Valo 		     struct ieee80211_sta *sta, u16 tid)
112105491d2cSKalle Valo {
112205491d2cSKalle Valo 	brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
112305491d2cSKalle Valo }
1124