1*6e778a7eSPedro F. Giffuni /*-
2*6e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
3*6e778a7eSPedro F. Giffuni *
414779705SSam Leffler * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
514779705SSam Leffler * Copyright (c) 2002-2008 Atheros Communications, Inc.
614779705SSam Leffler *
714779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler *
1114779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler
2114779705SSam Leffler #include "ah.h"
2214779705SSam Leffler #include "ah_internal.h"
2314779705SSam Leffler
2414779705SSam Leffler #include "ar5212/ar5212.h"
2514779705SSam Leffler #include "ar5212/ar5212reg.h"
2614779705SSam Leffler #include "ar5212/ar5212desc.h"
2714779705SSam Leffler
2814779705SSam Leffler /*
29fc4de9b7SAdrian Chadd * Return the hardware NextTBTT in TSF
30fc4de9b7SAdrian Chadd */
31fc4de9b7SAdrian Chadd uint64_t
ar5212GetNextTBTT(struct ath_hal * ah)32fc4de9b7SAdrian Chadd ar5212GetNextTBTT(struct ath_hal *ah)
33fc4de9b7SAdrian Chadd {
34fc4de9b7SAdrian Chadd #define TU_TO_TSF(_tu) (((uint64_t)(_tu)) << 10)
35fc4de9b7SAdrian Chadd return TU_TO_TSF(OS_REG_READ(ah, AR_TIMER0));
36fc4de9b7SAdrian Chadd #undef TU_TO_TSF
37fc4de9b7SAdrian Chadd }
38fc4de9b7SAdrian Chadd
39fc4de9b7SAdrian Chadd /*
4014779705SSam Leffler * Initialize all of the hardware registers used to
4114779705SSam Leffler * send beacons. Note that for station operation the
4214779705SSam Leffler * driver calls ar5212SetStaBeaconTimers instead.
4314779705SSam Leffler */
4414779705SSam Leffler void
ar5212SetBeaconTimers(struct ath_hal * ah,const HAL_BEACON_TIMERS * bt)4514779705SSam Leffler ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt)
4614779705SSam Leffler {
47c3f2102bSAdrian Chadd struct ath_hal_5212 *ahp = AH5212(ah);
4814779705SSam Leffler
492cb6e983SAdrian Chadd /*
502cb6e983SAdrian Chadd * Limit the timers to their specific resolutions:
512cb6e983SAdrian Chadd *
522cb6e983SAdrian Chadd * + Timer 0 - 0..15 0xffff TU
532cb6e983SAdrian Chadd * + Timer 1 - 0..18 0x7ffff TU/8
542cb6e983SAdrian Chadd * + Timer 2 - 0..24 0x1ffffff TU/8
552cb6e983SAdrian Chadd * + Timer 3 - 0..15 0xffff TU
562cb6e983SAdrian Chadd */
572cb6e983SAdrian Chadd OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt & 0xffff);
582cb6e983SAdrian Chadd OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba & 0x7ffff);
592cb6e983SAdrian Chadd OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba & 0x1ffffff);
602cb6e983SAdrian Chadd /* XXX force nextatim to be non-zero? */
612cb6e983SAdrian Chadd OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim & 0xffff);
6214779705SSam Leffler /*
6314779705SSam Leffler * Set the Beacon register after setting all timers.
6414779705SSam Leffler */
6514779705SSam Leffler if (bt->bt_intval & AR_BEACON_RESET_TSF) {
6614779705SSam Leffler /*
6714779705SSam Leffler * When resetting the TSF,
6814779705SSam Leffler * write twice to the corresponding register; each
6914779705SSam Leffler * write to the RESET_TSF bit toggles the internal
7014779705SSam Leffler * signal to cause a reset of the TSF - but if the signal
7114779705SSam Leffler * is left high, it will reset the TSF on the next
7214779705SSam Leffler * chip reset also! writing the bit an even number
7314779705SSam Leffler * of times fixes this issue
7414779705SSam Leffler */
7514779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_RESET_TSF);
7614779705SSam Leffler }
7714779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval);
78c3f2102bSAdrian Chadd ahp->ah_beaconInterval = (bt->bt_intval & HAL_BEACON_PERIOD);
7914779705SSam Leffler }
8014779705SSam Leffler
8114779705SSam Leffler /*
8214779705SSam Leffler * Old api for setting up beacon timer registers when
8314779705SSam Leffler * operating in !station mode. Note the fixed constants
8414779705SSam Leffler * adjusting the DBA and SWBA timers and the fixed ATIM
8514779705SSam Leffler * window.
8614779705SSam Leffler */
8714779705SSam Leffler void
ar5212BeaconInit(struct ath_hal * ah,uint32_t next_beacon,uint32_t beacon_period)8814779705SSam Leffler ar5212BeaconInit(struct ath_hal *ah,
8914779705SSam Leffler uint32_t next_beacon, uint32_t beacon_period)
9014779705SSam Leffler {
9114779705SSam Leffler HAL_BEACON_TIMERS bt;
9214779705SSam Leffler
9314779705SSam Leffler bt.bt_nexttbtt = next_beacon;
9414779705SSam Leffler /*
9514779705SSam Leffler * TIMER1: in AP/adhoc mode this controls the DMA beacon
9614779705SSam Leffler * alert timer; otherwise it controls the next wakeup time.
9714779705SSam Leffler * TIMER2: in AP mode, it controls the SBA beacon alert
9814779705SSam Leffler * interrupt; otherwise it sets the start of the next CFP.
9914779705SSam Leffler */
10014779705SSam Leffler switch (AH_PRIVATE(ah)->ah_opmode) {
10114779705SSam Leffler case HAL_M_STA:
10214779705SSam Leffler case HAL_M_MONITOR:
10314779705SSam Leffler bt.bt_nextdba = 0xffff;
10414779705SSam Leffler bt.bt_nextswba = 0x7ffff;
10514779705SSam Leffler break;
10614779705SSam Leffler case HAL_M_HOSTAP:
10714779705SSam Leffler case HAL_M_IBSS:
10814779705SSam Leffler bt.bt_nextdba = (next_beacon -
10937931a35SAdrian Chadd ah->ah_config.ah_dma_beacon_response_time) << 3; /* 1/8 TU */
11014779705SSam Leffler bt.bt_nextswba = (next_beacon -
11137931a35SAdrian Chadd ah->ah_config.ah_sw_beacon_response_time) << 3; /* 1/8 TU */
11214779705SSam Leffler break;
11314779705SSam Leffler }
11414779705SSam Leffler /*
11514779705SSam Leffler * Set the ATIM window
11614779705SSam Leffler * Our hardware does not support an ATIM window of 0
11714779705SSam Leffler * (beacons will not work). If the ATIM windows is 0,
11814779705SSam Leffler * force it to 1.
11914779705SSam Leffler */
12014779705SSam Leffler bt.bt_nextatim = next_beacon + 1;
12114779705SSam Leffler bt.bt_intval = beacon_period &
12214779705SSam Leffler (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN);
12314779705SSam Leffler ar5212SetBeaconTimers(ah, &bt);
12414779705SSam Leffler }
12514779705SSam Leffler
12614779705SSam Leffler void
ar5212ResetStaBeaconTimers(struct ath_hal * ah)12714779705SSam Leffler ar5212ResetStaBeaconTimers(struct ath_hal *ah)
12814779705SSam Leffler {
12914779705SSam Leffler uint32_t val;
13014779705SSam Leffler
13114779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER0, 0); /* no beacons */
13214779705SSam Leffler val = OS_REG_READ(ah, AR_STA_ID1);
13314779705SSam Leffler val |= AR_STA_ID1_PWR_SAV; /* XXX */
13414779705SSam Leffler /* tell the h/w that the associated AP is not PCF capable */
13514779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1,
13614779705SSam Leffler val & ~(AR_STA_ID1_USE_DEFANT | AR_STA_ID1_PCF));
13714779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_PERIOD);
13814779705SSam Leffler }
13914779705SSam Leffler
14014779705SSam Leffler /*
14114779705SSam Leffler * Set all the beacon related bits on the h/w for stations
14214779705SSam Leffler * i.e. initializes the corresponding h/w timers;
14314779705SSam Leffler * also tells the h/w whether to anticipate PCF beacons
14414779705SSam Leffler */
14514779705SSam Leffler void
ar5212SetStaBeaconTimers(struct ath_hal * ah,const HAL_BEACON_STATE * bs)14614779705SSam Leffler ar5212SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs)
14714779705SSam Leffler {
14814779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
14914779705SSam Leffler uint32_t nextTbtt, nextdtim,beaconintval, dtimperiod;
15014779705SSam Leffler
15114779705SSam Leffler HALASSERT(bs->bs_intval != 0);
15214779705SSam Leffler /* if the AP will do PCF */
15314779705SSam Leffler if (bs->bs_cfpmaxduration != 0) {
15414779705SSam Leffler /* tell the h/w that the associated AP is PCF capable */
15514779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1,
15614779705SSam Leffler OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PCF);
15714779705SSam Leffler
15814779705SSam Leffler /* set CFP_PERIOD(1.024ms) register */
15914779705SSam Leffler OS_REG_WRITE(ah, AR_CFP_PERIOD, bs->bs_cfpperiod);
16014779705SSam Leffler
16114779705SSam Leffler /* set CFP_DUR(1.024ms) register to max cfp duration */
16214779705SSam Leffler OS_REG_WRITE(ah, AR_CFP_DUR, bs->bs_cfpmaxduration);
16314779705SSam Leffler
16414779705SSam Leffler /* set TIMER2(128us) to anticipated time of next CFP */
16514779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER2, bs->bs_cfpnext << 3);
16614779705SSam Leffler } else {
16714779705SSam Leffler /* tell the h/w that the associated AP is not PCF capable */
16814779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1,
16914779705SSam Leffler OS_REG_READ(ah, AR_STA_ID1) &~ AR_STA_ID1_PCF);
17014779705SSam Leffler }
17114779705SSam Leffler
17214779705SSam Leffler /*
17314779705SSam Leffler * Set TIMER0(1.024ms) to the anticipated time of the next beacon.
17414779705SSam Leffler */
17514779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER0, bs->bs_nexttbtt);
17614779705SSam Leffler
17714779705SSam Leffler /*
17814779705SSam Leffler * Start the beacon timers by setting the BEACON register
17914779705SSam Leffler * to the beacon interval; also write the tim offset which
18014779705SSam Leffler * we should know by now. The code, in ar5211WriteAssocid,
18114779705SSam Leffler * also sets the tim offset once the AID is known which can
18214779705SSam Leffler * be left as such for now.
18314779705SSam Leffler */
18414779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON,
18514779705SSam Leffler (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_PERIOD|AR_BEACON_TIM))
18614779705SSam Leffler | SM(bs->bs_intval, AR_BEACON_PERIOD)
18714779705SSam Leffler | SM(bs->bs_timoffset ? bs->bs_timoffset + 4 : 0, AR_BEACON_TIM)
18814779705SSam Leffler );
18914779705SSam Leffler
19014779705SSam Leffler /*
19114779705SSam Leffler * Configure the BMISS interrupt. Note that we
19214779705SSam Leffler * assume the caller blocks interrupts while enabling
19314779705SSam Leffler * the threshold.
19414779705SSam Leffler */
19514779705SSam Leffler HALASSERT(bs->bs_bmissthreshold <= MS(0xffffffff, AR_RSSI_THR_BM_THR));
19614779705SSam Leffler ahp->ah_rssiThr = (ahp->ah_rssiThr &~ AR_RSSI_THR_BM_THR)
19714779705SSam Leffler | SM(bs->bs_bmissthreshold, AR_RSSI_THR_BM_THR);
19814779705SSam Leffler OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
19914779705SSam Leffler
20014779705SSam Leffler /*
20114779705SSam Leffler * Program the sleep registers to correlate with the beacon setup.
20214779705SSam Leffler */
20314779705SSam Leffler
20414779705SSam Leffler /*
20514779705SSam Leffler * Oahu beacons timers on the station were used for power
20614779705SSam Leffler * save operation (waking up in anticipation of a beacon)
20714779705SSam Leffler * and any CFP function; Venice does sleep/power-save timers
20814779705SSam Leffler * differently - so this is the right place to set them up;
20914779705SSam Leffler * don't think the beacon timers are used by venice sta hw
21014779705SSam Leffler * for any useful purpose anymore
21114779705SSam Leffler * Setup venice's sleep related timers
21214779705SSam Leffler * Current implementation assumes sw processing of beacons -
21314779705SSam Leffler * assuming an interrupt is generated every beacon which
21414779705SSam Leffler * causes the hardware to become awake until the sw tells
21514779705SSam Leffler * it to go to sleep again; beacon timeout is to allow for
21614779705SSam Leffler * beacon jitter; cab timeout is max time to wait for cab
21714779705SSam Leffler * after seeing the last DTIM or MORE CAB bit
21814779705SSam Leffler */
21914779705SSam Leffler #define CAB_TIMEOUT_VAL 10 /* in TU */
22014779705SSam Leffler #define BEACON_TIMEOUT_VAL 10 /* in TU */
22114779705SSam Leffler #define SLEEP_SLOP 3 /* in TU */
22214779705SSam Leffler
22314779705SSam Leffler /*
22414779705SSam Leffler * For max powersave mode we may want to sleep for longer than a
22514779705SSam Leffler * beacon period and not want to receive all beacons; modify the
22614779705SSam Leffler * timers accordingly; make sure to align the next TIM to the
22714779705SSam Leffler * next DTIM if we decide to wake for DTIMs only
22814779705SSam Leffler */
22914779705SSam Leffler beaconintval = bs->bs_intval & HAL_BEACON_PERIOD;
23014779705SSam Leffler HALASSERT(beaconintval != 0);
23114779705SSam Leffler if (bs->bs_sleepduration > beaconintval) {
23214779705SSam Leffler HALASSERT(roundup(bs->bs_sleepduration, beaconintval) ==
23314779705SSam Leffler bs->bs_sleepduration);
23414779705SSam Leffler beaconintval = bs->bs_sleepduration;
23514779705SSam Leffler }
23614779705SSam Leffler dtimperiod = bs->bs_dtimperiod;
23714779705SSam Leffler if (bs->bs_sleepduration > dtimperiod) {
23814779705SSam Leffler HALASSERT(dtimperiod == 0 ||
23914779705SSam Leffler roundup(bs->bs_sleepduration, dtimperiod) ==
24014779705SSam Leffler bs->bs_sleepduration);
24114779705SSam Leffler dtimperiod = bs->bs_sleepduration;
24214779705SSam Leffler }
24314779705SSam Leffler HALASSERT(beaconintval <= dtimperiod);
24414779705SSam Leffler if (beaconintval == dtimperiod)
24514779705SSam Leffler nextTbtt = bs->bs_nextdtim;
24614779705SSam Leffler else
24714779705SSam Leffler nextTbtt = bs->bs_nexttbtt;
24814779705SSam Leffler nextdtim = bs->bs_nextdtim;
24914779705SSam Leffler
25014779705SSam Leffler OS_REG_WRITE(ah, AR_SLEEP1,
25114779705SSam Leffler SM((nextdtim - SLEEP_SLOP) << 3, AR_SLEEP1_NEXT_DTIM)
25214779705SSam Leffler | SM(CAB_TIMEOUT_VAL, AR_SLEEP1_CAB_TIMEOUT)
25314779705SSam Leffler | AR_SLEEP1_ASSUME_DTIM
25414779705SSam Leffler | AR_SLEEP1_ENH_SLEEP_ENA
25514779705SSam Leffler );
25614779705SSam Leffler OS_REG_WRITE(ah, AR_SLEEP2,
25714779705SSam Leffler SM((nextTbtt - SLEEP_SLOP) << 3, AR_SLEEP2_NEXT_TIM)
25814779705SSam Leffler | SM(BEACON_TIMEOUT_VAL, AR_SLEEP2_BEACON_TIMEOUT)
25914779705SSam Leffler );
26014779705SSam Leffler OS_REG_WRITE(ah, AR_SLEEP3,
26114779705SSam Leffler SM(beaconintval, AR_SLEEP3_TIM_PERIOD)
26214779705SSam Leffler | SM(dtimperiod, AR_SLEEP3_DTIM_PERIOD)
26314779705SSam Leffler );
26414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n",
26514779705SSam Leffler __func__, bs->bs_nextdtim);
26614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n",
26714779705SSam Leffler __func__, nextTbtt);
26814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: beacon period %d\n",
26914779705SSam Leffler __func__, beaconintval);
27014779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: DTIM period %d\n",
27114779705SSam Leffler __func__, dtimperiod);
27214779705SSam Leffler #undef CAB_TIMEOUT_VAL
27314779705SSam Leffler #undef BEACON_TIMEOUT_VAL
27414779705SSam Leffler #undef SLEEP_SLOP
27514779705SSam Leffler }
276