18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
118c2ecf20Sopenharmony_ci * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
138c2ecf20Sopenharmony_ci * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
148c2ecf20Sopenharmony_ci * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#include <net/mac80211.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "rate.h"
198c2ecf20Sopenharmony_ci#include "scb.h"
208c2ecf20Sopenharmony_ci#include "phy/phy_hal.h"
218c2ecf20Sopenharmony_ci#include "antsel.h"
228c2ecf20Sopenharmony_ci#include "main.h"
238c2ecf20Sopenharmony_ci#include "ampdu.h"
248c2ecf20Sopenharmony_ci#include "debug.h"
258c2ecf20Sopenharmony_ci#include "brcms_trace_events.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* max number of mpdus in an ampdu */
288c2ecf20Sopenharmony_ci#define AMPDU_MAX_MPDU			32
298c2ecf20Sopenharmony_ci/* max number of mpdus in an ampdu to a legacy */
308c2ecf20Sopenharmony_ci#define AMPDU_NUM_MPDU_LEGACY		16
318c2ecf20Sopenharmony_ci/* max Tx ba window size (in pdu) */
328c2ecf20Sopenharmony_ci#define AMPDU_TX_BA_MAX_WSIZE		64
338c2ecf20Sopenharmony_ci/* default Tx ba window size (in pdu) */
348c2ecf20Sopenharmony_ci#define AMPDU_TX_BA_DEF_WSIZE		64
358c2ecf20Sopenharmony_ci/* default Rx ba window size (in pdu) */
368c2ecf20Sopenharmony_ci#define AMPDU_RX_BA_DEF_WSIZE		64
378c2ecf20Sopenharmony_ci/* max Rx ba window size (in pdu) */
388c2ecf20Sopenharmony_ci#define AMPDU_RX_BA_MAX_WSIZE		64
398c2ecf20Sopenharmony_ci/* max dur of tx ampdu (in msec) */
408c2ecf20Sopenharmony_ci#define	AMPDU_MAX_DUR			5
418c2ecf20Sopenharmony_ci/* default tx retry limit */
428c2ecf20Sopenharmony_ci#define AMPDU_DEF_RETRY_LIMIT		5
438c2ecf20Sopenharmony_ci/* default tx retry limit at reg rate */
448c2ecf20Sopenharmony_ci#define AMPDU_DEF_RR_RETRY_LIMIT	2
458c2ecf20Sopenharmony_ci/* default ffpld reserved bytes */
468c2ecf20Sopenharmony_ci#define AMPDU_DEF_FFPLD_RSVD		2048
478c2ecf20Sopenharmony_ci/* # of inis to be freed on detach */
488c2ecf20Sopenharmony_ci#define AMPDU_INI_FREE			10
498c2ecf20Sopenharmony_ci/* max # of mpdus released at a time */
508c2ecf20Sopenharmony_ci#define	AMPDU_SCB_MAX_RELEASE		20
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define NUM_FFPLD_FIFO 4	/* number of fifo concerned by pre-loading */
538c2ecf20Sopenharmony_ci#define FFPLD_TX_MAX_UNFL   200	/* default value of the average number of ampdu
548c2ecf20Sopenharmony_ci				 * without underflows
558c2ecf20Sopenharmony_ci				 */
568c2ecf20Sopenharmony_ci#define FFPLD_MPDU_SIZE 1800	/* estimate of maximum mpdu size */
578c2ecf20Sopenharmony_ci#define FFPLD_MAX_MCS 23	/* we don't deal with mcs 32 */
588c2ecf20Sopenharmony_ci#define FFPLD_PLD_INCR 1000	/* increments in bytes */
598c2ecf20Sopenharmony_ci#define FFPLD_MAX_AMPDU_CNT 5000	/* maximum number of ampdu we
608c2ecf20Sopenharmony_ci					 * accumulate between resets.
618c2ecf20Sopenharmony_ci					 */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define AMPDU_DELIMITER_LEN	4
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* max allowed number of mpdus in an ampdu (2 streams) */
668c2ecf20Sopenharmony_ci#define AMPDU_NUM_MPDU		16
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
718c2ecf20Sopenharmony_ci#define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
728c2ecf20Sopenharmony_ci	AMPDU_DELIMITER_LEN + 3\
738c2ecf20Sopenharmony_ci	+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* modulo add/sub, bound = 2^k */
768c2ecf20Sopenharmony_ci#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1))
778c2ecf20Sopenharmony_ci#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1))
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* structure to hold tx fifo information and pre-loading state
808c2ecf20Sopenharmony_ci * counters specific to tx underflows of ampdus
818c2ecf20Sopenharmony_ci * some counters might be redundant with the ones in wlc or ampdu structures.
828c2ecf20Sopenharmony_ci * This allows to maintain a specific state independently of
838c2ecf20Sopenharmony_ci * how often and/or when the wlc counters are updated.
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * ampdu_pld_size: number of bytes to be pre-loaded
868c2ecf20Sopenharmony_ci * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu
878c2ecf20Sopenharmony_ci * prev_txfunfl: num of underflows last read from the HW macstats counter
888c2ecf20Sopenharmony_ci * accum_txfunfl: num of underflows since we modified pld params
898c2ecf20Sopenharmony_ci * accum_txampdu: num of tx ampdu since we modified pld params
908c2ecf20Sopenharmony_ci * prev_txampdu: previous reading of tx ampdu
918c2ecf20Sopenharmony_ci * dmaxferrate: estimated dma avg xfer rate in kbits/sec
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_cistruct brcms_fifo_info {
948c2ecf20Sopenharmony_ci	u16 ampdu_pld_size;
958c2ecf20Sopenharmony_ci	u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1];
968c2ecf20Sopenharmony_ci	u16 prev_txfunfl;
978c2ecf20Sopenharmony_ci	u32 accum_txfunfl;
988c2ecf20Sopenharmony_ci	u32 accum_txampdu;
998c2ecf20Sopenharmony_ci	u32 prev_txampdu;
1008c2ecf20Sopenharmony_ci	u32 dmaxferrate;
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* AMPDU module specific state
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * wlc: pointer to main wlc structure
1068c2ecf20Sopenharmony_ci * scb_handle: scb cubby handle to retrieve data from scb
1078c2ecf20Sopenharmony_ci * ini_enable: per-tid initiator enable/disable of ampdu
1088c2ecf20Sopenharmony_ci * ba_tx_wsize: Tx ba window size (in pdu)
1098c2ecf20Sopenharmony_ci * ba_rx_wsize: Rx ba window size (in pdu)
1108c2ecf20Sopenharmony_ci * retry_limit: mpdu transmit retry limit
1118c2ecf20Sopenharmony_ci * rr_retry_limit: mpdu transmit retry limit at regular rate
1128c2ecf20Sopenharmony_ci * retry_limit_tid: per-tid mpdu transmit retry limit
1138c2ecf20Sopenharmony_ci * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate
1148c2ecf20Sopenharmony_ci * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec
1158c2ecf20Sopenharmony_ci * max_pdu: max pdus allowed in ampdu
1168c2ecf20Sopenharmony_ci * dur: max duration of an ampdu (in msec)
1178c2ecf20Sopenharmony_ci * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes
1188c2ecf20Sopenharmony_ci * ffpld_rsvd: number of bytes to reserve for preload
1198c2ecf20Sopenharmony_ci * max_txlen: max size of ampdu per mcs, bw and sgi
1208c2ecf20Sopenharmony_ci * mfbr: enable multiple fallback rate
1218c2ecf20Sopenharmony_ci * tx_max_funl: underflows should be kept such that
1228c2ecf20Sopenharmony_ci *		(tx_max_funfl*underflows) < tx frames
1238c2ecf20Sopenharmony_ci * fifo_tb: table of fifo infos
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistruct ampdu_info {
1268c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc;
1278c2ecf20Sopenharmony_ci	int scb_handle;
1288c2ecf20Sopenharmony_ci	u8 ini_enable[AMPDU_MAX_SCB_TID];
1298c2ecf20Sopenharmony_ci	u8 ba_tx_wsize;
1308c2ecf20Sopenharmony_ci	u8 ba_rx_wsize;
1318c2ecf20Sopenharmony_ci	u8 retry_limit;
1328c2ecf20Sopenharmony_ci	u8 rr_retry_limit;
1338c2ecf20Sopenharmony_ci	u8 retry_limit_tid[AMPDU_MAX_SCB_TID];
1348c2ecf20Sopenharmony_ci	u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
1358c2ecf20Sopenharmony_ci	u8 mpdu_density;
1368c2ecf20Sopenharmony_ci	s8 max_pdu;
1378c2ecf20Sopenharmony_ci	u8 dur;
1388c2ecf20Sopenharmony_ci	u8 rx_factor;
1398c2ecf20Sopenharmony_ci	u32 ffpld_rsvd;
1408c2ecf20Sopenharmony_ci	u32 max_txlen[MCS_TABLE_SIZE][2][2];
1418c2ecf20Sopenharmony_ci	bool mfbr;
1428c2ecf20Sopenharmony_ci	u32 tx_max_funl;
1438c2ecf20Sopenharmony_ci	struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO];
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* used for flushing ampdu packets */
1478c2ecf20Sopenharmony_cistruct cb_del_ampdu_pars {
1488c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
1498c2ecf20Sopenharmony_ci	u16 tid;
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u32 rate, mcs;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
1578c2ecf20Sopenharmony_ci		/* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
1588c2ecf20Sopenharmony_ci		/* 20MHz, No SGI */
1598c2ecf20Sopenharmony_ci		rate = mcs_2_rate(mcs, false, false);
1608c2ecf20Sopenharmony_ci		ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
1618c2ecf20Sopenharmony_ci		/* 40 MHz, No SGI */
1628c2ecf20Sopenharmony_ci		rate = mcs_2_rate(mcs, true, false);
1638c2ecf20Sopenharmony_ci		ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
1648c2ecf20Sopenharmony_ci		/* 20MHz, SGI */
1658c2ecf20Sopenharmony_ci		rate = mcs_2_rate(mcs, false, true);
1668c2ecf20Sopenharmony_ci		ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
1678c2ecf20Sopenharmony_ci		/* 40 MHz, SGI */
1688c2ecf20Sopenharmony_ci		rate = mcs_2_rate(mcs, true, true);
1698c2ecf20Sopenharmony_ci		ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic bool brcms_c_ampdu_cap(struct ampdu_info *ampdu)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	if (BRCMS_PHY_11N_CAP(ampdu->wlc->band))
1768c2ecf20Sopenharmony_ci		return true;
1778c2ecf20Sopenharmony_ci	else
1788c2ecf20Sopenharmony_ci		return false;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = ampdu->wlc;
1848c2ecf20Sopenharmony_ci	struct bcma_device *core = wlc->hw->d11core;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	wlc->pub->_ampdu = false;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (on) {
1898c2ecf20Sopenharmony_ci		if (!(wlc->pub->_n_enab & SUPPORT_11N)) {
1908c2ecf20Sopenharmony_ci			brcms_err(core, "wl%d: driver not nmode enabled\n",
1918c2ecf20Sopenharmony_ci				  wlc->pub->unit);
1928c2ecf20Sopenharmony_ci			return -ENOTSUPP;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci		if (!brcms_c_ampdu_cap(ampdu)) {
1958c2ecf20Sopenharmony_ci			brcms_err(core, "wl%d: device not ampdu capable\n",
1968c2ecf20Sopenharmony_ci				  wlc->pub->unit);
1978c2ecf20Sopenharmony_ci			return -ENOTSUPP;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		wlc->pub->_ampdu = on;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void brcms_c_ffpld_init(struct ampdu_info *ampdu)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	int i, j;
2088c2ecf20Sopenharmony_ci	struct brcms_fifo_info *fifo;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	for (j = 0; j < NUM_FFPLD_FIFO; j++) {
2118c2ecf20Sopenharmony_ci		fifo = (ampdu->fifo_tb + j);
2128c2ecf20Sopenharmony_ci		fifo->ampdu_pld_size = 0;
2138c2ecf20Sopenharmony_ci		for (i = 0; i <= FFPLD_MAX_MCS; i++)
2148c2ecf20Sopenharmony_ci			fifo->mcs2ampdu_table[i] = 255;
2158c2ecf20Sopenharmony_ci		fifo->dmaxferrate = 0;
2168c2ecf20Sopenharmony_ci		fifo->accum_txampdu = 0;
2178c2ecf20Sopenharmony_ci		fifo->prev_txfunfl = 0;
2188c2ecf20Sopenharmony_ci		fifo->accum_txfunfl = 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistruct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct ampdu_info *ampdu;
2268c2ecf20Sopenharmony_ci	int i;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
2298c2ecf20Sopenharmony_ci	if (!ampdu)
2308c2ecf20Sopenharmony_ci		return NULL;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ampdu->wlc = wlc;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
2358c2ecf20Sopenharmony_ci		ampdu->ini_enable[i] = true;
2368c2ecf20Sopenharmony_ci	/* Disable ampdu for VO by default */
2378c2ecf20Sopenharmony_ci	ampdu->ini_enable[PRIO_8021D_VO] = false;
2388c2ecf20Sopenharmony_ci	ampdu->ini_enable[PRIO_8021D_NC] = false;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Disable ampdu for BK by default since not enough fifo space */
2418c2ecf20Sopenharmony_ci	ampdu->ini_enable[PRIO_8021D_NONE] = false;
2428c2ecf20Sopenharmony_ci	ampdu->ini_enable[PRIO_8021D_BK] = false;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
2458c2ecf20Sopenharmony_ci	ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
2468c2ecf20Sopenharmony_ci	ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
2478c2ecf20Sopenharmony_ci	ampdu->max_pdu = AUTO;
2488c2ecf20Sopenharmony_ci	ampdu->dur = AMPDU_MAX_DUR;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
2518c2ecf20Sopenharmony_ci	/*
2528c2ecf20Sopenharmony_ci	 * bump max ampdu rcv size to 64k for all 11n
2538c2ecf20Sopenharmony_ci	 * devices except 4321A0 and 4321A1
2548c2ecf20Sopenharmony_ci	 */
2558c2ecf20Sopenharmony_ci	if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
2568c2ecf20Sopenharmony_ci		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K;
2578c2ecf20Sopenharmony_ci	else
2588c2ecf20Sopenharmony_ci		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
2598c2ecf20Sopenharmony_ci	ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
2608c2ecf20Sopenharmony_ci	ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
2638c2ecf20Sopenharmony_ci		ampdu->retry_limit_tid[i] = ampdu->retry_limit;
2648c2ecf20Sopenharmony_ci		ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur);
2688c2ecf20Sopenharmony_ci	ampdu->mfbr = false;
2698c2ecf20Sopenharmony_ci	/* try to set ampdu to the default value */
2708c2ecf20Sopenharmony_ci	brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
2738c2ecf20Sopenharmony_ci	brcms_c_ffpld_init(ampdu);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return ampdu;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_civoid brcms_c_ampdu_detach(struct ampdu_info *ampdu)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	kfree(ampdu);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu,
2848c2ecf20Sopenharmony_ci					    struct scb *scb)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
2878c2ecf20Sopenharmony_ci	int i;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	scb_ampdu->max_pdu = AMPDU_NUM_MPDU;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* go back to legacy size if some preloading is occurring */
2928c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_FFPLD_FIFO; i++) {
2938c2ecf20Sopenharmony_ci		if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
2948c2ecf20Sopenharmony_ci			scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* apply user override */
2988c2ecf20Sopenharmony_ci	if (ampdu->max_pdu != AUTO)
2998c2ecf20Sopenharmony_ci		scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu,
3028c2ecf20Sopenharmony_ci				   AMPDU_SCB_MAX_RELEASE);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (scb_ampdu->max_rx_ampdu_bytes)
3058c2ecf20Sopenharmony_ci		scb_ampdu->release = min_t(u8, scb_ampdu->release,
3068c2ecf20Sopenharmony_ci			scb_ampdu->max_rx_ampdu_bytes / 1600);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	scb_ampdu->release = min(scb_ampdu->release,
3098c2ecf20Sopenharmony_ci				 ampdu->fifo_tb[TX_AC_BE_FIFO].
3108c2ecf20Sopenharmony_ci				 mcs2ampdu_table[FFPLD_MAX_MCS]);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	brcms_c_scb_ampdu_update_config(ampdu, &ampdu->wlc->pri_scb);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int i;
3218c2ecf20Sopenharmony_ci	u32 phy_rate, dma_rate, tmp;
3228c2ecf20Sopenharmony_ci	u8 max_mpdu;
3238c2ecf20Sopenharmony_ci	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* recompute the dma rate */
3268c2ecf20Sopenharmony_ci	/* note : we divide/multiply by 100 to avoid integer overflows */
3278c2ecf20Sopenharmony_ci	max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
3288c2ecf20Sopenharmony_ci			 AMPDU_NUM_MPDU_LEGACY);
3298c2ecf20Sopenharmony_ci	phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
3308c2ecf20Sopenharmony_ci	dma_rate =
3318c2ecf20Sopenharmony_ci	    (((phy_rate / 100) *
3328c2ecf20Sopenharmony_ci	      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
3338c2ecf20Sopenharmony_ci	     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
3348c2ecf20Sopenharmony_ci	fifo->dmaxferrate = dma_rate;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* fill up the mcs2ampdu table; do not recalc the last mcs */
3378c2ecf20Sopenharmony_ci	dma_rate = dma_rate >> 7;
3388c2ecf20Sopenharmony_ci	for (i = 0; i < FFPLD_MAX_MCS; i++) {
3398c2ecf20Sopenharmony_ci		/* shifting to keep it within integer range */
3408c2ecf20Sopenharmony_ci		phy_rate = mcs_2_rate(i, true, false) >> 7;
3418c2ecf20Sopenharmony_ci		if (phy_rate > dma_rate) {
3428c2ecf20Sopenharmony_ci			tmp = ((fifo->ampdu_pld_size * phy_rate) /
3438c2ecf20Sopenharmony_ci			       ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
3448c2ecf20Sopenharmony_ci			tmp = min_t(u32, tmp, 255);
3458c2ecf20Sopenharmony_ci			fifo->mcs2ampdu_table[i] = (u8) tmp;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/* evaluate the dma transfer rate using the tx underflows as feedback.
3518c2ecf20Sopenharmony_ci * If necessary, increase tx fifo preloading. If not enough,
3528c2ecf20Sopenharmony_ci * decrease maximum ampdu size for each mcs till underflows stop
3538c2ecf20Sopenharmony_ci * Return 1 if pre-loading not active, -1 if not an underflow event,
3548c2ecf20Sopenharmony_ci * 0 if pre-loading module took care of the event.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_cistatic int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct ampdu_info *ampdu = wlc->ampdu;
3598c2ecf20Sopenharmony_ci	u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false);
3608c2ecf20Sopenharmony_ci	u32 txunfl_ratio;
3618c2ecf20Sopenharmony_ci	u8 max_mpdu;
3628c2ecf20Sopenharmony_ci	u32 current_ampdu_cnt = 0;
3638c2ecf20Sopenharmony_ci	u16 max_pld_size;
3648c2ecf20Sopenharmony_ci	u32 new_txunfl;
3658c2ecf20Sopenharmony_ci	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid);
3668c2ecf20Sopenharmony_ci	uint xmtfifo_sz;
3678c2ecf20Sopenharmony_ci	u16 cur_txunfl;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* return if we got here for a different reason than underflows */
3708c2ecf20Sopenharmony_ci	cur_txunfl = brcms_b_read_shm(wlc->hw,
3718c2ecf20Sopenharmony_ci				      M_UCODE_MACSTAT +
3728c2ecf20Sopenharmony_ci				      offsetof(struct macstat, txfunfl[fid]));
3738c2ecf20Sopenharmony_ci	new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
3748c2ecf20Sopenharmony_ci	if (new_txunfl == 0) {
3758c2ecf20Sopenharmony_ci		brcms_dbg_ht(wlc->hw->d11core,
3768c2ecf20Sopenharmony_ci			     "TX status FRAG set but no tx underflows\n");
3778c2ecf20Sopenharmony_ci		return -1;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci	fifo->prev_txfunfl = cur_txunfl;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (!ampdu->tx_max_funl)
3828c2ecf20Sopenharmony_ci		return 1;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* check if fifo is big enough */
3858c2ecf20Sopenharmony_ci	if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz))
3868c2ecf20Sopenharmony_ci		return -1;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
3898c2ecf20Sopenharmony_ci		return 1;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
3928c2ecf20Sopenharmony_ci	fifo->accum_txfunfl += new_txunfl;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* we need to wait for at least 10 underflows */
3958c2ecf20Sopenharmony_ci	if (fifo->accum_txfunfl < 10)
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d  tx_underflows %d\n",
3998c2ecf20Sopenharmony_ci		     current_ampdu_cnt, fifo->accum_txfunfl);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/*
4028c2ecf20Sopenharmony_ci	   compute the current ratio of tx unfl per ampdu.
4038c2ecf20Sopenharmony_ci	   When the current ampdu count becomes too
4048c2ecf20Sopenharmony_ci	   big while the ratio remains small, we reset
4058c2ecf20Sopenharmony_ci	   the current count in order to not
4068c2ecf20Sopenharmony_ci	   introduce too big of a latency in detecting a
4078c2ecf20Sopenharmony_ci	   large amount of tx underflows later.
4088c2ecf20Sopenharmony_ci	 */
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (txunfl_ratio > ampdu->tx_max_funl) {
4138c2ecf20Sopenharmony_ci		if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT)
4148c2ecf20Sopenharmony_ci			fifo->accum_txfunfl = 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		return 0;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS],
4198c2ecf20Sopenharmony_ci			 AMPDU_NUM_MPDU_LEGACY);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* In case max value max_pdu is already lower than
4228c2ecf20Sopenharmony_ci	   the fifo depth, there is nothing more we can do.
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
4268c2ecf20Sopenharmony_ci		fifo->accum_txfunfl = 0;
4278c2ecf20Sopenharmony_ci		return 0;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (fifo->ampdu_pld_size < max_pld_size) {
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		/* increment by TX_FIFO_PLD_INC bytes */
4338c2ecf20Sopenharmony_ci		fifo->ampdu_pld_size += FFPLD_PLD_INCR;
4348c2ecf20Sopenharmony_ci		if (fifo->ampdu_pld_size > max_pld_size)
4358c2ecf20Sopenharmony_ci			fifo->ampdu_pld_size = max_pld_size;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		/* update scb release size */
4388c2ecf20Sopenharmony_ci		brcms_c_scb_ampdu_update_config_all(ampdu);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		/*
4418c2ecf20Sopenharmony_ci		 * compute a new dma xfer rate for max_mpdu @ max mcs.
4428c2ecf20Sopenharmony_ci		 * This is the minimum dma rate that can achieve no
4438c2ecf20Sopenharmony_ci		 * underflow condition for the current mpdu size.
4448c2ecf20Sopenharmony_ci		 *
4458c2ecf20Sopenharmony_ci		 * note : we divide/multiply by 100 to avoid integer overflows
4468c2ecf20Sopenharmony_ci		 */
4478c2ecf20Sopenharmony_ci		fifo->dmaxferrate =
4488c2ecf20Sopenharmony_ci		    (((phy_rate / 100) *
4498c2ecf20Sopenharmony_ci		      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
4508c2ecf20Sopenharmony_ci		     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		brcms_dbg_ht(wlc->hw->d11core,
4538c2ecf20Sopenharmony_ci			     "DMA estimated transfer rate %d; "
4548c2ecf20Sopenharmony_ci			     "pre-load size %d\n",
4558c2ecf20Sopenharmony_ci			     fifo->dmaxferrate, fifo->ampdu_pld_size);
4568c2ecf20Sopenharmony_ci	} else {
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		/* decrease ampdu size */
4598c2ecf20Sopenharmony_ci		if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
4608c2ecf20Sopenharmony_ci			if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
4618c2ecf20Sopenharmony_ci				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
4628c2ecf20Sopenharmony_ci				    AMPDU_NUM_MPDU_LEGACY - 1;
4638c2ecf20Sopenharmony_ci			else
4648c2ecf20Sopenharmony_ci				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci			/* recompute the table */
4678c2ecf20Sopenharmony_ci			brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci			/* update scb release size */
4708c2ecf20Sopenharmony_ci			brcms_c_scb_ampdu_update_config_all(ampdu);
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	fifo->accum_txfunfl = 0;
4748c2ecf20Sopenharmony_ci	return 0;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_civoid
4788c2ecf20Sopenharmony_cibrcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
4798c2ecf20Sopenharmony_ci	u8 ba_wsize,		/* negotiated ba window size (in pdu) */
4808c2ecf20Sopenharmony_ci	uint max_rx_ampdu_bytes) /* from ht_cap in beacon */
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct scb_ampdu *scb_ampdu;
4838c2ecf20Sopenharmony_ci	struct scb_ampdu_tid_ini *ini;
4848c2ecf20Sopenharmony_ci	struct ampdu_info *ampdu = wlc->ampdu;
4858c2ecf20Sopenharmony_ci	struct scb *scb = &wlc->pri_scb;
4868c2ecf20Sopenharmony_ci	scb_ampdu = &scb->scb_ampdu;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (!ampdu->ini_enable[tid]) {
4898c2ecf20Sopenharmony_ci		brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n",
4908c2ecf20Sopenharmony_ci			  __func__, tid);
4918c2ecf20Sopenharmony_ci		return;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	ini = &scb_ampdu->ini[tid];
4958c2ecf20Sopenharmony_ci	ini->tid = tid;
4968c2ecf20Sopenharmony_ci	ini->scb = scb_ampdu->scb;
4978c2ecf20Sopenharmony_ci	ini->ba_wsize = ba_wsize;
4988c2ecf20Sopenharmony_ci	scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_civoid brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
5028c2ecf20Sopenharmony_ci				 struct brcms_c_info *wlc)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	session->wlc = wlc;
5058c2ecf20Sopenharmony_ci	skb_queue_head_init(&session->skb_list);
5068c2ecf20Sopenharmony_ci	session->max_ampdu_len = 0;    /* determined from first MPDU */
5078c2ecf20Sopenharmony_ci	session->max_ampdu_frames = 0; /* determined from first MPDU */
5088c2ecf20Sopenharmony_ci	session->ampdu_len = 0;
5098c2ecf20Sopenharmony_ci	session->dma_len = 0;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/*
5138c2ecf20Sopenharmony_ci * Preps the given packet for AMPDU based on the session data. If the
5148c2ecf20Sopenharmony_ci * frame cannot be accomodated in the current session, -ENOSPC is
5158c2ecf20Sopenharmony_ci * returned.
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_ciint brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
5188c2ecf20Sopenharmony_ci			    struct sk_buff *p)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = session->wlc;
5218c2ecf20Sopenharmony_ci	struct ampdu_info *ampdu = wlc->ampdu;
5228c2ecf20Sopenharmony_ci	struct scb *scb = &wlc->pri_scb;
5238c2ecf20Sopenharmony_ci	struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
5248c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
5258c2ecf20Sopenharmony_ci	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
5268c2ecf20Sopenharmony_ci	struct d11txh *txh = (struct d11txh *)p->data;
5278c2ecf20Sopenharmony_ci	unsigned ampdu_frames;
5288c2ecf20Sopenharmony_ci	u8 ndelim, tid;
5298c2ecf20Sopenharmony_ci	u8 *plcp;
5308c2ecf20Sopenharmony_ci	uint len;
5318c2ecf20Sopenharmony_ci	u16 mcl;
5328c2ecf20Sopenharmony_ci	bool fbr_iscck;
5338c2ecf20Sopenharmony_ci	bool rr;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
5368c2ecf20Sopenharmony_ci	plcp = (u8 *)(txh + 1);
5378c2ecf20Sopenharmony_ci	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
5388c2ecf20Sopenharmony_ci	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
5398c2ecf20Sopenharmony_ci			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
5408c2ecf20Sopenharmony_ci	len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	ampdu_frames = skb_queue_len(&session->skb_list);
5438c2ecf20Sopenharmony_ci	if (ampdu_frames != 0) {
5448c2ecf20Sopenharmony_ci		struct sk_buff *first;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		if (ampdu_frames + 1 > session->max_ampdu_frames ||
5478c2ecf20Sopenharmony_ci		    session->ampdu_len + len > session->max_ampdu_len)
5488c2ecf20Sopenharmony_ci			return -ENOSPC;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/*
5518c2ecf20Sopenharmony_ci		 * We aren't really out of space if the new frame is of
5528c2ecf20Sopenharmony_ci		 * a different priority, but we want the same behaviour
5538c2ecf20Sopenharmony_ci		 * so return -ENOSPC anyway.
5548c2ecf20Sopenharmony_ci		 *
5558c2ecf20Sopenharmony_ci		 * XXX: The old AMPDU code did this, but is it really
5568c2ecf20Sopenharmony_ci		 * necessary?
5578c2ecf20Sopenharmony_ci		 */
5588c2ecf20Sopenharmony_ci		first = skb_peek(&session->skb_list);
5598c2ecf20Sopenharmony_ci		if (p->priority != first->priority)
5608c2ecf20Sopenharmony_ci			return -ENOSPC;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/*
5648c2ecf20Sopenharmony_ci	 * Now that we're sure this frame can be accomodated, update the
5658c2ecf20Sopenharmony_ci	 * session information.
5668c2ecf20Sopenharmony_ci	 */
5678c2ecf20Sopenharmony_ci	session->ampdu_len += len;
5688c2ecf20Sopenharmony_ci	session->dma_len += p->len;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	tid = (u8)p->priority;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Handle retry limits */
5738c2ecf20Sopenharmony_ci	if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) {
5748c2ecf20Sopenharmony_ci		txrate[0].count++;
5758c2ecf20Sopenharmony_ci		rr = true;
5768c2ecf20Sopenharmony_ci	} else {
5778c2ecf20Sopenharmony_ci		txrate[1].count++;
5788c2ecf20Sopenharmony_ci		rr = false;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (ampdu_frames == 0) {
5828c2ecf20Sopenharmony_ci		u8 plcp0, plcp3, is40, sgi, mcs;
5838c2ecf20Sopenharmony_ci		uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
5848c2ecf20Sopenharmony_ci		struct brcms_fifo_info *f = &ampdu->fifo_tb[fifo];
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		if (rr) {
5878c2ecf20Sopenharmony_ci			plcp0 = plcp[0];
5888c2ecf20Sopenharmony_ci			plcp3 = plcp[3];
5898c2ecf20Sopenharmony_ci		} else {
5908c2ecf20Sopenharmony_ci			plcp0 = txh->FragPLCPFallback[0];
5918c2ecf20Sopenharmony_ci			plcp3 = txh->FragPLCPFallback[3];
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		/* Limit AMPDU size based on MCS */
5968c2ecf20Sopenharmony_ci		is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
5978c2ecf20Sopenharmony_ci		sgi = plcp3_issgi(plcp3) ? 1 : 0;
5988c2ecf20Sopenharmony_ci		mcs = plcp0 & ~MIMO_PLCP_40MHZ;
5998c2ecf20Sopenharmony_ci		session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes,
6008c2ecf20Sopenharmony_ci					     ampdu->max_txlen[mcs][is40][sgi]);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		session->max_ampdu_frames = scb_ampdu->max_pdu;
6038c2ecf20Sopenharmony_ci		if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
6048c2ecf20Sopenharmony_ci			session->max_ampdu_frames =
6058c2ecf20Sopenharmony_ci				min_t(u16, f->mcs2ampdu_table[mcs],
6068c2ecf20Sopenharmony_ci				      session->max_ampdu_frames);
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	/*
6118c2ecf20Sopenharmony_ci	 * Treat all frames as "middle" frames of AMPDU here. First and
6128c2ecf20Sopenharmony_ci	 * last frames must be fixed up after all MPDUs have been prepped.
6138c2ecf20Sopenharmony_ci	 */
6148c2ecf20Sopenharmony_ci	mcl = le16_to_cpu(txh->MacTxControlLow);
6158c2ecf20Sopenharmony_ci	mcl &= ~TXC_AMPDU_MASK;
6168c2ecf20Sopenharmony_ci	mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
6178c2ecf20Sopenharmony_ci	mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
6188c2ecf20Sopenharmony_ci	txh->MacTxControlLow = cpu_to_le16(mcl);
6198c2ecf20Sopenharmony_ci	txh->PreloadSize = 0;	/* always default to 0 */
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	skb_queue_tail(&session->skb_list, p);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_civoid brcms_c_ampdu_finalize(struct brcms_ampdu_session *session)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = session->wlc;
6298c2ecf20Sopenharmony_ci	struct ampdu_info *ampdu = wlc->ampdu;
6308c2ecf20Sopenharmony_ci	struct sk_buff *first, *last;
6318c2ecf20Sopenharmony_ci	struct d11txh *txh;
6328c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info;
6338c2ecf20Sopenharmony_ci	struct ieee80211_tx_rate *txrate;
6348c2ecf20Sopenharmony_ci	u8 ndelim;
6358c2ecf20Sopenharmony_ci	u8 *plcp;
6368c2ecf20Sopenharmony_ci	uint len;
6378c2ecf20Sopenharmony_ci	uint fifo;
6388c2ecf20Sopenharmony_ci	struct brcms_fifo_info *f;
6398c2ecf20Sopenharmony_ci	u16 mcl;
6408c2ecf20Sopenharmony_ci	bool fbr;
6418c2ecf20Sopenharmony_ci	bool fbr_iscck;
6428c2ecf20Sopenharmony_ci	struct ieee80211_rts *rts;
6438c2ecf20Sopenharmony_ci	bool use_rts = false, use_cts = false;
6448c2ecf20Sopenharmony_ci	u16 dma_len = session->dma_len;
6458c2ecf20Sopenharmony_ci	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
6468c2ecf20Sopenharmony_ci	u32 rspec = 0, rspec_fallback = 0;
6478c2ecf20Sopenharmony_ci	u32 rts_rspec = 0, rts_rspec_fallback = 0;
6488c2ecf20Sopenharmony_ci	u8 plcp0, is40, mcs;
6498c2ecf20Sopenharmony_ci	u16 mch;
6508c2ecf20Sopenharmony_ci	u8 preamble_type = BRCMS_GF_PREAMBLE;
6518c2ecf20Sopenharmony_ci	u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
6528c2ecf20Sopenharmony_ci	u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
6538c2ecf20Sopenharmony_ci	u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (skb_queue_empty(&session->skb_list))
6568c2ecf20Sopenharmony_ci		return;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	first = skb_peek(&session->skb_list);
6598c2ecf20Sopenharmony_ci	last = skb_peek_tail(&session->skb_list);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* Need to fix up last MPDU first to adjust AMPDU length */
6628c2ecf20Sopenharmony_ci	txh = (struct d11txh *)last->data;
6638c2ecf20Sopenharmony_ci	fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
6648c2ecf20Sopenharmony_ci	f = &ampdu->fifo_tb[fifo];
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	mcl = le16_to_cpu(txh->MacTxControlLow);
6678c2ecf20Sopenharmony_ci	mcl &= ~TXC_AMPDU_MASK;
6688c2ecf20Sopenharmony_ci	mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
6698c2ecf20Sopenharmony_ci	txh->MacTxControlLow = cpu_to_le16(mcl);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* remove the null delimiter after last mpdu */
6728c2ecf20Sopenharmony_ci	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
6738c2ecf20Sopenharmony_ci	txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
6748c2ecf20Sopenharmony_ci	session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* remove the pad len from last mpdu */
6778c2ecf20Sopenharmony_ci	fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
6788c2ecf20Sopenharmony_ci	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
6798c2ecf20Sopenharmony_ci			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
6808c2ecf20Sopenharmony_ci	session->ampdu_len -= roundup(len, 4) - len;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	/* Now fix up the first MPDU */
6838c2ecf20Sopenharmony_ci	tx_info = IEEE80211_SKB_CB(first);
6848c2ecf20Sopenharmony_ci	txrate = tx_info->status.rates;
6858c2ecf20Sopenharmony_ci	txh = (struct d11txh *)first->data;
6868c2ecf20Sopenharmony_ci	plcp = (u8 *)(txh + 1);
6878c2ecf20Sopenharmony_ci	rts = (struct ieee80211_rts *)&txh->rts_frame;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	mcl = le16_to_cpu(txh->MacTxControlLow);
6908c2ecf20Sopenharmony_ci	/* If only one MPDU leave it marked as last */
6918c2ecf20Sopenharmony_ci	if (first != last) {
6928c2ecf20Sopenharmony_ci		mcl &= ~TXC_AMPDU_MASK;
6938c2ecf20Sopenharmony_ci		mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	mcl |= TXC_STARTMSDU;
6968c2ecf20Sopenharmony_ci	if (ieee80211_is_rts(rts->frame_control)) {
6978c2ecf20Sopenharmony_ci		mcl |= TXC_SENDRTS;
6988c2ecf20Sopenharmony_ci		use_rts = true;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	if (ieee80211_is_cts(rts->frame_control)) {
7018c2ecf20Sopenharmony_ci		mcl |= TXC_SENDCTS;
7028c2ecf20Sopenharmony_ci		use_cts = true;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci	txh->MacTxControlLow = cpu_to_le16(mcl);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	fbr = txrate[1].count > 0;
7078c2ecf20Sopenharmony_ci	if (!fbr)
7088c2ecf20Sopenharmony_ci		plcp0 = plcp[0];
7098c2ecf20Sopenharmony_ci	else
7108c2ecf20Sopenharmony_ci		plcp0 = txh->FragPLCPFallback[0];
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
7138c2ecf20Sopenharmony_ci	mcs = plcp0 & ~MIMO_PLCP_40MHZ;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (is40) {
7168c2ecf20Sopenharmony_ci		if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi)))
7178c2ecf20Sopenharmony_ci			mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP;
7188c2ecf20Sopenharmony_ci		else
7198c2ecf20Sopenharmony_ci			mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* rebuild the rspec and rspec_fallback */
7238c2ecf20Sopenharmony_ci	rspec = RSPEC_MIMORATE;
7248c2ecf20Sopenharmony_ci	rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
7258c2ecf20Sopenharmony_ci	if (plcp[0] & MIMO_PLCP_40MHZ)
7268c2ecf20Sopenharmony_ci		rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
7298c2ecf20Sopenharmony_ci	if (fbr_iscck) {
7308c2ecf20Sopenharmony_ci		rspec_fallback =
7318c2ecf20Sopenharmony_ci			cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0]));
7328c2ecf20Sopenharmony_ci	} else {
7338c2ecf20Sopenharmony_ci		rspec_fallback = RSPEC_MIMORATE;
7348c2ecf20Sopenharmony_ci		rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
7358c2ecf20Sopenharmony_ci		if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
7368c2ecf20Sopenharmony_ci			rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT;
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (use_rts || use_cts) {
7408c2ecf20Sopenharmony_ci		rts_rspec =
7418c2ecf20Sopenharmony_ci			brcms_c_rspec_to_rts_rspec(wlc, rspec,
7428c2ecf20Sopenharmony_ci						   false, mimo_ctlchbw);
7438c2ecf20Sopenharmony_ci		rts_rspec_fallback =
7448c2ecf20Sopenharmony_ci			brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback,
7458c2ecf20Sopenharmony_ci						   false, mimo_ctlchbw);
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len);
7498c2ecf20Sopenharmony_ci	/* mark plcp to indicate ampdu */
7508c2ecf20Sopenharmony_ci	BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* reset the mixed mode header durations */
7538c2ecf20Sopenharmony_ci	if (txh->MModeLen) {
7548c2ecf20Sopenharmony_ci		u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec,
7558c2ecf20Sopenharmony_ci						     session->ampdu_len);
7568c2ecf20Sopenharmony_ci		txh->MModeLen = cpu_to_le16(mmodelen);
7578c2ecf20Sopenharmony_ci		preamble_type = BRCMS_MM_PREAMBLE;
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci	if (txh->MModeFbrLen) {
7608c2ecf20Sopenharmony_ci		u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback,
7618c2ecf20Sopenharmony_ci						     session->ampdu_len);
7628c2ecf20Sopenharmony_ci		txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
7638c2ecf20Sopenharmony_ci		fbr_preamble_type = BRCMS_MM_PREAMBLE;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* set the preload length */
7678c2ecf20Sopenharmony_ci	if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
7688c2ecf20Sopenharmony_ci		dma_len = min(dma_len, f->ampdu_pld_size);
7698c2ecf20Sopenharmony_ci		txh->PreloadSize = cpu_to_le16(dma_len);
7708c2ecf20Sopenharmony_ci	} else {
7718c2ecf20Sopenharmony_ci		txh->PreloadSize = 0;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	mch = le16_to_cpu(txh->MacTxControlHigh);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* update RTS dur fields */
7778c2ecf20Sopenharmony_ci	if (use_rts || use_cts) {
7788c2ecf20Sopenharmony_ci		u16 durid;
7798c2ecf20Sopenharmony_ci		if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
7808c2ecf20Sopenharmony_ci		    TXC_PREAMBLE_RTS_MAIN_SHORT)
7818c2ecf20Sopenharmony_ci			rts_preamble_type = BRCMS_SHORT_PREAMBLE;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
7848c2ecf20Sopenharmony_ci		     TXC_PREAMBLE_RTS_FB_SHORT)
7858c2ecf20Sopenharmony_ci			rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
7888c2ecf20Sopenharmony_ci						   rspec, rts_preamble_type,
7898c2ecf20Sopenharmony_ci						   preamble_type,
7908c2ecf20Sopenharmony_ci						   session->ampdu_len, true);
7918c2ecf20Sopenharmony_ci		rts->duration = cpu_to_le16(durid);
7928c2ecf20Sopenharmony_ci		durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
7938c2ecf20Sopenharmony_ci						   rts_rspec_fallback,
7948c2ecf20Sopenharmony_ci						   rspec_fallback,
7958c2ecf20Sopenharmony_ci						   rts_fbr_preamble_type,
7968c2ecf20Sopenharmony_ci						   fbr_preamble_type,
7978c2ecf20Sopenharmony_ci						   session->ampdu_len, true);
7988c2ecf20Sopenharmony_ci		txh->RTSDurFallback = cpu_to_le16(durid);
7998c2ecf20Sopenharmony_ci		/* set TxFesTimeNormal */
8008c2ecf20Sopenharmony_ci		txh->TxFesTimeNormal = rts->duration;
8018c2ecf20Sopenharmony_ci		/* set fallback rate version of TxFesTimeNormal */
8028c2ecf20Sopenharmony_ci		txh->TxFesTimeFallback = txh->RTSDurFallback;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* set flag and plcp for fallback rate */
8068c2ecf20Sopenharmony_ci	if (fbr) {
8078c2ecf20Sopenharmony_ci		mch |= TXC_AMPDU_FBR;
8088c2ecf20Sopenharmony_ci		txh->MacTxControlHigh = cpu_to_le16(mch);
8098c2ecf20Sopenharmony_ci		BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
8108c2ecf20Sopenharmony_ci		BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n",
8148c2ecf20Sopenharmony_ci		     wlc->pub->unit, skb_queue_len(&session->skb_list),
8158c2ecf20Sopenharmony_ci		     session->ampdu_len);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic void
8198c2ecf20Sopenharmony_cibrcms_c_ampdu_rate_status(struct brcms_c_info *wlc,
8208c2ecf20Sopenharmony_ci			  struct ieee80211_tx_info *tx_info,
8218c2ecf20Sopenharmony_ci			  struct tx_status *txs, u8 mcs)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
8248c2ecf20Sopenharmony_ci	int i;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* clear the rest of the rates */
8278c2ecf20Sopenharmony_ci	for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
8288c2ecf20Sopenharmony_ci		txrate[i].idx = -1;
8298c2ecf20Sopenharmony_ci		txrate[i].count = 0;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic void
8348c2ecf20Sopenharmony_cibrcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
8358c2ecf20Sopenharmony_ci			      struct sk_buff *p, struct tx_status *txs,
8368c2ecf20Sopenharmony_ci			      u32 s1, u32 s2)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct scb_ampdu *scb_ampdu;
8398c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = ampdu->wlc;
8408c2ecf20Sopenharmony_ci	struct scb_ampdu_tid_ini *ini;
8418c2ecf20Sopenharmony_ci	u8 bitmap[8], queue, tid;
8428c2ecf20Sopenharmony_ci	struct d11txh *txh;
8438c2ecf20Sopenharmony_ci	u8 *plcp;
8448c2ecf20Sopenharmony_ci	struct ieee80211_hdr *h;
8458c2ecf20Sopenharmony_ci	u16 seq, start_seq = 0, bindex, index, mcl;
8468c2ecf20Sopenharmony_ci	u8 mcs = 0;
8478c2ecf20Sopenharmony_ci	bool ba_recd = false, ack_recd = false;
8488c2ecf20Sopenharmony_ci	u8 suc_mpdu = 0, tot_mpdu = 0;
8498c2ecf20Sopenharmony_ci	uint supr_status;
8508c2ecf20Sopenharmony_ci	bool retry = true;
8518c2ecf20Sopenharmony_ci	u16 mimoantsel = 0;
8528c2ecf20Sopenharmony_ci	u8 retry_limit;
8538c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci#ifdef DEBUG
8568c2ecf20Sopenharmony_ci	u8 hole[AMPDU_MAX_MPDU];
8578c2ecf20Sopenharmony_ci	memset(hole, 0, sizeof(hole));
8588c2ecf20Sopenharmony_ci#endif
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	scb_ampdu = &scb->scb_ampdu;
8618c2ecf20Sopenharmony_ci	tid = (u8) (p->priority);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	ini = &scb_ampdu->ini[tid];
8648c2ecf20Sopenharmony_ci	retry_limit = ampdu->retry_limit_tid[tid];
8658c2ecf20Sopenharmony_ci	memset(bitmap, 0, sizeof(bitmap));
8668c2ecf20Sopenharmony_ci	queue = txs->frameid & TXFID_QUEUE_MASK;
8678c2ecf20Sopenharmony_ci	supr_status = txs->status & TX_STATUS_SUPR_MASK;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (txs->status & TX_STATUS_ACK_RCV) {
8708c2ecf20Sopenharmony_ci		WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE));
8718c2ecf20Sopenharmony_ci		start_seq = txs->sequence >> SEQNUM_SHIFT;
8728c2ecf20Sopenharmony_ci		bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
8738c2ecf20Sopenharmony_ci		    TX_STATUS_BA_BMAP03_SHIFT;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		WARN_ON(s1 & TX_STATUS_INTERMEDIATE);
8768c2ecf20Sopenharmony_ci		WARN_ON(!(s1 & TX_STATUS_AMPDU));
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		bitmap[0] |=
8798c2ecf20Sopenharmony_ci		    (s1 & TX_STATUS_BA_BMAP47_MASK) <<
8808c2ecf20Sopenharmony_ci		    TX_STATUS_BA_BMAP47_SHIFT;
8818c2ecf20Sopenharmony_ci		bitmap[1] = (s1 >> 8) & 0xff;
8828c2ecf20Sopenharmony_ci		bitmap[2] = (s1 >> 16) & 0xff;
8838c2ecf20Sopenharmony_ci		bitmap[3] = (s1 >> 24) & 0xff;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci		bitmap[4] = s2 & 0xff;
8868c2ecf20Sopenharmony_ci		bitmap[5] = (s2 >> 8) & 0xff;
8878c2ecf20Sopenharmony_ci		bitmap[6] = (s2 >> 16) & 0xff;
8888c2ecf20Sopenharmony_ci		bitmap[7] = (s2 >> 24) & 0xff;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci		ba_recd = true;
8918c2ecf20Sopenharmony_ci	} else {
8928c2ecf20Sopenharmony_ci		if (supr_status) {
8938c2ecf20Sopenharmony_ci			if (supr_status == TX_STATUS_SUPR_BADCH) {
8948c2ecf20Sopenharmony_ci				brcms_dbg_ht(wlc->hw->d11core,
8958c2ecf20Sopenharmony_ci					  "%s: Pkt tx suppressed, illegal channel possibly %d\n",
8968c2ecf20Sopenharmony_ci					  __func__, CHSPEC_CHANNEL(
8978c2ecf20Sopenharmony_ci					  wlc->default_bss->chanspec));
8988c2ecf20Sopenharmony_ci			} else {
8998c2ecf20Sopenharmony_ci				if (supr_status != TX_STATUS_SUPR_FRAG)
9008c2ecf20Sopenharmony_ci					brcms_err(wlc->hw->d11core,
9018c2ecf20Sopenharmony_ci						  "%s: supr_status 0x%x\n",
9028c2ecf20Sopenharmony_ci						  __func__, supr_status);
9038c2ecf20Sopenharmony_ci			}
9048c2ecf20Sopenharmony_ci			/* no need to retry for badch; will fail again */
9058c2ecf20Sopenharmony_ci			if (supr_status == TX_STATUS_SUPR_BADCH ||
9068c2ecf20Sopenharmony_ci			    supr_status == TX_STATUS_SUPR_EXPTIME) {
9078c2ecf20Sopenharmony_ci				retry = false;
9088c2ecf20Sopenharmony_ci			} else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
9098c2ecf20Sopenharmony_ci				/* TX underflow:
9108c2ecf20Sopenharmony_ci				 *   try tuning pre-loading or ampdu size
9118c2ecf20Sopenharmony_ci				 */
9128c2ecf20Sopenharmony_ci			} else if (supr_status == TX_STATUS_SUPR_FRAG) {
9138c2ecf20Sopenharmony_ci				/*
9148c2ecf20Sopenharmony_ci				 * if there were underflows, but pre-loading
9158c2ecf20Sopenharmony_ci				 * is not active, notify rate adaptation.
9168c2ecf20Sopenharmony_ci				 */
9178c2ecf20Sopenharmony_ci				brcms_c_ffpld_check_txfunfl(wlc, queue);
9188c2ecf20Sopenharmony_ci			}
9198c2ecf20Sopenharmony_ci		} else if (txs->phyerr) {
9208c2ecf20Sopenharmony_ci			brcms_dbg_ht(wlc->hw->d11core,
9218c2ecf20Sopenharmony_ci				     "%s: ampdu tx phy error (0x%x)\n",
9228c2ecf20Sopenharmony_ci				     __func__, txs->phyerr);
9238c2ecf20Sopenharmony_ci		}
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* loop through all pkts and retry if not acked */
9278c2ecf20Sopenharmony_ci	while (p) {
9288c2ecf20Sopenharmony_ci		tx_info = IEEE80211_SKB_CB(p);
9298c2ecf20Sopenharmony_ci		txh = (struct d11txh *) p->data;
9308c2ecf20Sopenharmony_ci		mcl = le16_to_cpu(txh->MacTxControlLow);
9318c2ecf20Sopenharmony_ci		plcp = (u8 *) (txh + 1);
9328c2ecf20Sopenharmony_ci		h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
9338c2ecf20Sopenharmony_ci		seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh));
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		if (tot_mpdu == 0) {
9388c2ecf20Sopenharmony_ci			mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
9398c2ecf20Sopenharmony_ci			mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel);
9408c2ecf20Sopenharmony_ci		}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci		index = TX_SEQ_TO_INDEX(seq);
9438c2ecf20Sopenharmony_ci		ack_recd = false;
9448c2ecf20Sopenharmony_ci		if (ba_recd) {
9458c2ecf20Sopenharmony_ci			bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
9468c2ecf20Sopenharmony_ci			brcms_dbg_ht(wlc->hw->d11core,
9478c2ecf20Sopenharmony_ci				     "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n",
9488c2ecf20Sopenharmony_ci				     tid, seq, start_seq, bindex,
9498c2ecf20Sopenharmony_ci				     isset(bitmap, bindex), index);
9508c2ecf20Sopenharmony_ci			/* if acked then clear bit and free packet */
9518c2ecf20Sopenharmony_ci			if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
9528c2ecf20Sopenharmony_ci			    && isset(bitmap, bindex)) {
9538c2ecf20Sopenharmony_ci				ini->txretry[index] = 0;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci				/*
9568c2ecf20Sopenharmony_ci				 * ampdu_ack_len:
9578c2ecf20Sopenharmony_ci				 *   number of acked aggregated frames
9588c2ecf20Sopenharmony_ci				 */
9598c2ecf20Sopenharmony_ci				/* ampdu_len: number of aggregated frames */
9608c2ecf20Sopenharmony_ci				brcms_c_ampdu_rate_status(wlc, tx_info, txs,
9618c2ecf20Sopenharmony_ci							  mcs);
9628c2ecf20Sopenharmony_ci				tx_info->flags |= IEEE80211_TX_STAT_ACK;
9638c2ecf20Sopenharmony_ci				tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
9648c2ecf20Sopenharmony_ci				tx_info->status.ampdu_ack_len =
9658c2ecf20Sopenharmony_ci					tx_info->status.ampdu_len = 1;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci				skb_pull(p, D11_PHY_HDR_LEN);
9688c2ecf20Sopenharmony_ci				skb_pull(p, D11_TXH_LEN);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
9718c2ecf20Sopenharmony_ci							    p);
9728c2ecf20Sopenharmony_ci				ack_recd = true;
9738c2ecf20Sopenharmony_ci				suc_mpdu++;
9748c2ecf20Sopenharmony_ci			}
9758c2ecf20Sopenharmony_ci		}
9768c2ecf20Sopenharmony_ci		/* either retransmit or send bar if ack not recd */
9778c2ecf20Sopenharmony_ci		if (!ack_recd) {
9788c2ecf20Sopenharmony_ci			if (retry && (ini->txretry[index] < (int)retry_limit)) {
9798c2ecf20Sopenharmony_ci				int ret;
9808c2ecf20Sopenharmony_ci				ini->txretry[index]++;
9818c2ecf20Sopenharmony_ci				ret = brcms_c_txfifo(wlc, queue, p);
9828c2ecf20Sopenharmony_ci				/*
9838c2ecf20Sopenharmony_ci				 * We shouldn't be out of space in the DMA
9848c2ecf20Sopenharmony_ci				 * ring here since we're reinserting a frame
9858c2ecf20Sopenharmony_ci				 * that was just pulled out.
9868c2ecf20Sopenharmony_ci				 */
9878c2ecf20Sopenharmony_ci				WARN_ONCE(ret, "queue %d out of txds\n", queue);
9888c2ecf20Sopenharmony_ci			} else {
9898c2ecf20Sopenharmony_ci				/* Retry timeout */
9908c2ecf20Sopenharmony_ci				ieee80211_tx_info_clear_status(tx_info);
9918c2ecf20Sopenharmony_ci				tx_info->status.ampdu_ack_len = 0;
9928c2ecf20Sopenharmony_ci				tx_info->status.ampdu_len = 1;
9938c2ecf20Sopenharmony_ci				tx_info->flags |=
9948c2ecf20Sopenharmony_ci				    IEEE80211_TX_STAT_AMPDU_NO_BACK;
9958c2ecf20Sopenharmony_ci				skb_pull(p, D11_PHY_HDR_LEN);
9968c2ecf20Sopenharmony_ci				skb_pull(p, D11_TXH_LEN);
9978c2ecf20Sopenharmony_ci				brcms_dbg_ht(wlc->hw->d11core,
9988c2ecf20Sopenharmony_ci					     "BA Timeout, seq %d\n",
9998c2ecf20Sopenharmony_ci					     seq);
10008c2ecf20Sopenharmony_ci				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
10018c2ecf20Sopenharmony_ci							    p);
10028c2ecf20Sopenharmony_ci			}
10038c2ecf20Sopenharmony_ci		}
10048c2ecf20Sopenharmony_ci		tot_mpdu++;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci		/* break out if last packet of ampdu */
10078c2ecf20Sopenharmony_ci		if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
10088c2ecf20Sopenharmony_ci		    TXC_AMPDU_LAST)
10098c2ecf20Sopenharmony_ci			break;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* update rate state */
10158c2ecf20Sopenharmony_ci	brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_civoid
10198c2ecf20Sopenharmony_cibrcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
10208c2ecf20Sopenharmony_ci		     struct sk_buff *p, struct tx_status *txs)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = ampdu->wlc;
10238c2ecf20Sopenharmony_ci	u32 s1 = 0, s2 = 0;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* BMAC_NOTE: For the split driver, second level txstatus comes later
10268c2ecf20Sopenharmony_ci	 * So if the ACK was received then wait for the second level else just
10278c2ecf20Sopenharmony_ci	 * call the first one
10288c2ecf20Sopenharmony_ci	 */
10298c2ecf20Sopenharmony_ci	if (txs->status & TX_STATUS_ACK_RCV) {
10308c2ecf20Sopenharmony_ci		u8 status_delay = 0;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci		/* wait till the next 8 bytes of txstatus is available */
10338c2ecf20Sopenharmony_ci		s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus));
10348c2ecf20Sopenharmony_ci		while ((s1 & TXS_V) == 0) {
10358c2ecf20Sopenharmony_ci			udelay(1);
10368c2ecf20Sopenharmony_ci			status_delay++;
10378c2ecf20Sopenharmony_ci			if (status_delay > 10)
10388c2ecf20Sopenharmony_ci				return; /* error condition */
10398c2ecf20Sopenharmony_ci			s1 = bcma_read32(wlc->hw->d11core,
10408c2ecf20Sopenharmony_ci					 D11REGOFFS(frmtxstatus));
10418c2ecf20Sopenharmony_ci		}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2));
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (scb) {
10478c2ecf20Sopenharmony_ci		brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
10488c2ecf20Sopenharmony_ci	} else {
10498c2ecf20Sopenharmony_ci		/* loop through all pkts and free */
10508c2ecf20Sopenharmony_ci		u8 queue = txs->frameid & TXFID_QUEUE_MASK;
10518c2ecf20Sopenharmony_ci		struct d11txh *txh;
10528c2ecf20Sopenharmony_ci		u16 mcl;
10538c2ecf20Sopenharmony_ci		while (p) {
10548c2ecf20Sopenharmony_ci			txh = (struct d11txh *) p->data;
10558c2ecf20Sopenharmony_ci			trace_brcms_txdesc(&wlc->hw->d11core->dev, txh,
10568c2ecf20Sopenharmony_ci					   sizeof(*txh));
10578c2ecf20Sopenharmony_ci			mcl = le16_to_cpu(txh->MacTxControlLow);
10588c2ecf20Sopenharmony_ci			brcmu_pkt_buf_free_skb(p);
10598c2ecf20Sopenharmony_ci			/* break out if last packet of ampdu */
10608c2ecf20Sopenharmony_ci			if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
10618c2ecf20Sopenharmony_ci			    TXC_AMPDU_LAST)
10628c2ecf20Sopenharmony_ci				break;
10638c2ecf20Sopenharmony_ci			p = dma_getnexttxp(wlc->hw->di[queue],
10648c2ecf20Sopenharmony_ci					   DMA_RANGE_TRANSMITTED);
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_civoid brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	char template[T_RAM_ACCESS_SZ * 2];
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/* driver needs to write the ta in the template; ta is at offset 16 */
10748c2ecf20Sopenharmony_ci	memset(template, 0, sizeof(template));
10758c2ecf20Sopenharmony_ci	memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
10768c2ecf20Sopenharmony_ci	brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16),
10778c2ecf20Sopenharmony_ci				  (T_RAM_ACCESS_SZ * 2),
10788c2ecf20Sopenharmony_ci				  template);
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cibool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	return wlc->ampdu->ini_enable[tid];
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_civoid brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct brcms_c_info *wlc = ampdu->wlc;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	/*
10918c2ecf20Sopenharmony_ci	 * Extend ucode internal watchdog timer to
10928c2ecf20Sopenharmony_ci	 * match larger received frames
10938c2ecf20Sopenharmony_ci	 */
10948c2ecf20Sopenharmony_ci	if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
10958c2ecf20Sopenharmony_ci	    IEEE80211_HT_MAX_AMPDU_64K) {
10968c2ecf20Sopenharmony_ci		brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
10978c2ecf20Sopenharmony_ci		brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
10988c2ecf20Sopenharmony_ci	} else {
10998c2ecf20Sopenharmony_ci		brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
11008c2ecf20Sopenharmony_ci		brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci/*
11058c2ecf20Sopenharmony_ci * callback function that helps invalidating ampdu packets in a DMA queue
11068c2ecf20Sopenharmony_ci */
11078c2ecf20Sopenharmony_cistatic void dma_cb_fn_ampdu(void *txi, void *arg_a)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta = arg_a;
11108c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
11138c2ecf20Sopenharmony_ci	    (tx_info->rate_driver_data[0] == sta || sta == NULL))
11148c2ecf20Sopenharmony_ci		tx_info->rate_driver_data[0] = NULL;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci/*
11188c2ecf20Sopenharmony_ci * When a remote party is no longer available for ampdu communication, any
11198c2ecf20Sopenharmony_ci * pending tx ampdu packets in the driver have to be flushed.
11208c2ecf20Sopenharmony_ci */
11218c2ecf20Sopenharmony_civoid brcms_c_ampdu_flush(struct brcms_c_info *wlc,
11228c2ecf20Sopenharmony_ci		     struct ieee80211_sta *sta, u16 tid)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
11258c2ecf20Sopenharmony_ci}
1126