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, &du->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 = &du->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 = &du->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