xref: /dflybsd-src/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_beacon.c (revision a20e5e5100e71be018e157de86834f5dc77186c3)
1b7d5e03cSMatthew Dillon /*
2b7d5e03cSMatthew Dillon  * Copyright (c) 2013 Qualcomm Atheros, Inc.
3b7d5e03cSMatthew Dillon  *
4b7d5e03cSMatthew Dillon  * Permission to use, copy, modify, and/or distribute this software for any
5b7d5e03cSMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
6b7d5e03cSMatthew Dillon  * copyright notice and this permission notice appear in all copies.
7b7d5e03cSMatthew Dillon  *
8b7d5e03cSMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9b7d5e03cSMatthew Dillon  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10b7d5e03cSMatthew Dillon  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11b7d5e03cSMatthew Dillon  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12b7d5e03cSMatthew Dillon  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13b7d5e03cSMatthew Dillon  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14b7d5e03cSMatthew Dillon  * PERFORMANCE OF THIS SOFTWARE.
15b7d5e03cSMatthew Dillon  */
16b7d5e03cSMatthew Dillon 
17b7d5e03cSMatthew Dillon #include "opt_ah.h"
18b7d5e03cSMatthew Dillon 
19b7d5e03cSMatthew Dillon #include "ah.h"
20b7d5e03cSMatthew Dillon #include "ah_internal.h"
21b7d5e03cSMatthew Dillon 
22b7d5e03cSMatthew Dillon #include "ar9300/ar9300.h"
23b7d5e03cSMatthew Dillon #include "ar9300/ar9300reg.h"
24b7d5e03cSMatthew Dillon 
25b7d5e03cSMatthew Dillon #define TU_TO_USEC(_tu) ((_tu) << 10)
26b7d5e03cSMatthew Dillon #define	ONE_EIGHTH_TU_TO_USEC(_tu8) ((_tu8) << 7)
27b7d5e03cSMatthew Dillon 
28b7d5e03cSMatthew Dillon extern u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q);
29b7d5e03cSMatthew Dillon 
30b7d5e03cSMatthew Dillon /*
31b7d5e03cSMatthew Dillon  * Initializes all of the hardware registers used to
32b7d5e03cSMatthew Dillon  * send beacons.  Note that for station operation the
33b7d5e03cSMatthew Dillon  * driver calls ar9300_set_sta_beacon_timers instead.
34b7d5e03cSMatthew Dillon  */
35b7d5e03cSMatthew Dillon void
ar9300_beacon_init(struct ath_hal * ah,u_int32_t next_beacon,u_int32_t beacon_period,u_int32_t beacon_period_fraction,HAL_OPMODE opmode)36b7d5e03cSMatthew Dillon ar9300_beacon_init(struct ath_hal *ah,
37*a20e5e51SMatthew Dillon     u_int32_t next_beacon, u_int32_t beacon_period,
38*a20e5e51SMatthew Dillon     u_int32_t beacon_period_fraction, HAL_OPMODE opmode)
39b7d5e03cSMatthew Dillon {
40b7d5e03cSMatthew Dillon     u_int32_t               beacon_period_usec;
41b7d5e03cSMatthew Dillon 
42b7d5e03cSMatthew Dillon     HALASSERT(opmode == HAL_M_IBSS || opmode == HAL_M_HOSTAP);
43b7d5e03cSMatthew Dillon     if (opmode == HAL_M_IBSS) {
44b7d5e03cSMatthew Dillon         OS_REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
45b7d5e03cSMatthew Dillon     }
46b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, ONE_EIGHTH_TU_TO_USEC(next_beacon));
47b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT,
48b7d5e03cSMatthew Dillon         (ONE_EIGHTH_TU_TO_USEC(next_beacon) -
49b7d5e03cSMatthew Dillon         ah->ah_config.ah_dma_beacon_response_time));
50b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_SWBA,
51b7d5e03cSMatthew Dillon         (ONE_EIGHTH_TU_TO_USEC(next_beacon) -
52b7d5e03cSMatthew Dillon         ah->ah_config.ah_sw_beacon_response_time));
53b7d5e03cSMatthew Dillon 
54b7d5e03cSMatthew Dillon     beacon_period_usec =
55b7d5e03cSMatthew Dillon         ONE_EIGHTH_TU_TO_USEC(beacon_period & HAL_BEACON_PERIOD_TU8);
56*a20e5e51SMatthew Dillon 
57*a20e5e51SMatthew Dillon     /* Add the fraction adjustment lost due to unit conversions. */
58*a20e5e51SMatthew Dillon     beacon_period_usec += beacon_period_fraction;
59*a20e5e51SMatthew Dillon 
60*a20e5e51SMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_BEACON,
61*a20e5e51SMatthew Dillon         "%s: next_beacon=0x%08x, beacon_period=%d, opmode=%d, beacon_period_usec=%d\n",
62*a20e5e51SMatthew Dillon         __func__, next_beacon, beacon_period, opmode, beacon_period_usec);
63*a20e5e51SMatthew Dillon 
64b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period_usec);
65b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period_usec);
66b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period_usec);
67b7d5e03cSMatthew Dillon 
68b7d5e03cSMatthew Dillon     /* reset TSF if required */
69b7d5e03cSMatthew Dillon     if (beacon_period & HAL_BEACON_RESET_TSF) {
70b7d5e03cSMatthew Dillon         ar9300_reset_tsf(ah);
71b7d5e03cSMatthew Dillon     }
72b7d5e03cSMatthew Dillon 
73b7d5e03cSMatthew Dillon     /* enable timers */
74b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_TIMER_MODE,
75b7d5e03cSMatthew Dillon         AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN);
76b7d5e03cSMatthew Dillon }
77b7d5e03cSMatthew Dillon 
78b7d5e03cSMatthew Dillon /*
79b7d5e03cSMatthew Dillon  * Set all the beacon related bits on the h/w for stations
80b7d5e03cSMatthew Dillon  * i.e. initializes the corresponding h/w timers;
81b7d5e03cSMatthew Dillon  */
82b7d5e03cSMatthew Dillon void
ar9300_set_sta_beacon_timers(struct ath_hal * ah,const HAL_BEACON_STATE * bs)83b7d5e03cSMatthew Dillon ar9300_set_sta_beacon_timers(struct ath_hal *ah, const HAL_BEACON_STATE *bs)
84b7d5e03cSMatthew Dillon {
85b7d5e03cSMatthew Dillon     u_int32_t next_tbtt, beaconintval, dtimperiod, beacontimeout;
86b7d5e03cSMatthew Dillon     HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps;
87b7d5e03cSMatthew Dillon 
88b7d5e03cSMatthew Dillon     HALASSERT(bs->bs_intval != 0);
89b7d5e03cSMatthew Dillon 
90b7d5e03cSMatthew Dillon     /* no cfp setting since h/w automatically takes care */
91b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
92b7d5e03cSMatthew Dillon 
93b7d5e03cSMatthew Dillon     /*
94b7d5e03cSMatthew Dillon      * Start the beacon timers by setting the BEACON register
95b7d5e03cSMatthew Dillon      * to the beacon interval; no need to write tim offset since
96b7d5e03cSMatthew Dillon      * h/w parses IEs.
97b7d5e03cSMatthew Dillon      */
98b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_BEACON_PERIOD,
99b7d5e03cSMatthew Dillon                  TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD));
100b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_DMA_BEACON_PERIOD,
101b7d5e03cSMatthew Dillon                  TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD));
102b7d5e03cSMatthew Dillon     /*
103b7d5e03cSMatthew Dillon      * Configure the BMISS interrupt.  Note that we
104b7d5e03cSMatthew Dillon      * assume the caller blocks interrupts while enabling
105b7d5e03cSMatthew Dillon      * the threshold.
106b7d5e03cSMatthew Dillon      */
107b7d5e03cSMatthew Dillon     HALASSERT(bs->bs_bmissthreshold <=
108b7d5e03cSMatthew Dillon         (AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S));
109b7d5e03cSMatthew Dillon     OS_REG_RMW_FIELD(ah, AR_RSSI_THR,
110b7d5e03cSMatthew Dillon         AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold);
111b7d5e03cSMatthew Dillon 
112b7d5e03cSMatthew Dillon     /*
113b7d5e03cSMatthew Dillon      * Program the sleep registers to correlate with the beacon setup.
114b7d5e03cSMatthew Dillon      */
115b7d5e03cSMatthew Dillon 
116b7d5e03cSMatthew Dillon     /*
117b7d5e03cSMatthew Dillon      * Current implementation assumes sw processing of beacons -
118b7d5e03cSMatthew Dillon      * assuming an interrupt is generated every beacon which
119b7d5e03cSMatthew Dillon      * causes the hardware to become awake until the sw tells
120b7d5e03cSMatthew Dillon      * it to go to sleep again; beacon timeout is to allow for
121b7d5e03cSMatthew Dillon      * beacon jitter; cab timeout is max time to wait for cab
122b7d5e03cSMatthew Dillon      * after seeing the last DTIM or MORE CAB bit
123b7d5e03cSMatthew Dillon      */
124b7d5e03cSMatthew Dillon #define CAB_TIMEOUT_VAL         10 /* in TU */
125b7d5e03cSMatthew Dillon #define BEACON_TIMEOUT_VAL      10 /* in TU */
126b7d5e03cSMatthew Dillon #define MIN_BEACON_TIMEOUT_VAL   1 /* in 1/8 TU */
127b7d5e03cSMatthew Dillon #define SLEEP_SLOP               3 /* in TU */
128b7d5e03cSMatthew Dillon 
129b7d5e03cSMatthew Dillon     /*
130b7d5e03cSMatthew Dillon      * For max powersave mode we may want to sleep for longer than a
131b7d5e03cSMatthew Dillon      * beacon period and not want to receive all beacons; modify the
132b7d5e03cSMatthew Dillon      * timers accordingly; make sure to align the next TIM to the
133b7d5e03cSMatthew Dillon      * next DTIM if we decide to wake for DTIMs only
134b7d5e03cSMatthew Dillon      */
135b7d5e03cSMatthew Dillon     beaconintval = bs->bs_intval & HAL_BEACON_PERIOD;
136b7d5e03cSMatthew Dillon     HALASSERT(beaconintval != 0);
137b7d5e03cSMatthew Dillon     if (bs->bs_sleepduration > beaconintval) {
138b7d5e03cSMatthew Dillon         HALASSERT(roundup(bs->bs_sleepduration, beaconintval) ==
139b7d5e03cSMatthew Dillon                 bs->bs_sleepduration);
140b7d5e03cSMatthew Dillon         beaconintval = bs->bs_sleepduration;
141b7d5e03cSMatthew Dillon     }
142b7d5e03cSMatthew Dillon     dtimperiod = bs->bs_dtimperiod;
143b7d5e03cSMatthew Dillon     if (bs->bs_sleepduration > dtimperiod) {
144b7d5e03cSMatthew Dillon         HALASSERT(dtimperiod == 0 ||
145b7d5e03cSMatthew Dillon             roundup(bs->bs_sleepduration, dtimperiod) ==
146b7d5e03cSMatthew Dillon                 bs->bs_sleepduration);
147b7d5e03cSMatthew Dillon         dtimperiod = bs->bs_sleepduration;
148b7d5e03cSMatthew Dillon     }
149b7d5e03cSMatthew Dillon     HALASSERT(beaconintval <= dtimperiod);
150b7d5e03cSMatthew Dillon     if (beaconintval == dtimperiod) {
151b7d5e03cSMatthew Dillon         next_tbtt = bs->bs_nextdtim;
152b7d5e03cSMatthew Dillon     } else {
153b7d5e03cSMatthew Dillon         next_tbtt = bs->bs_nexttbtt;
154b7d5e03cSMatthew Dillon     }
155b7d5e03cSMatthew Dillon 
156b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_BEACON,
157b7d5e03cSMatthew Dillon         "%s: next DTIM %d\n", __func__, bs->bs_nextdtim);
158b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_BEACON,
159b7d5e03cSMatthew Dillon         "%s: next beacon %d\n", __func__, next_tbtt);
160b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_BEACON,
161b7d5e03cSMatthew Dillon         "%s: beacon period %d\n", __func__, beaconintval);
162b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_BEACON,
163b7d5e03cSMatthew Dillon         "%s: DTIM period %d\n", __func__, dtimperiod);
164b7d5e03cSMatthew Dillon 
165b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_DTIM, TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP));
166b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(next_tbtt - SLEEP_SLOP));
167b7d5e03cSMatthew Dillon 
168b7d5e03cSMatthew Dillon     /* cab timeout is now in 1/8 TU */
169b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_SLEEP1,
170b7d5e03cSMatthew Dillon         SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT)
171b7d5e03cSMatthew Dillon         | AR_SLEEP1_ASSUME_DTIM);
172b7d5e03cSMatthew Dillon 
173b7d5e03cSMatthew Dillon     /* beacon timeout is now in 1/8 TU */
174b7d5e03cSMatthew Dillon     if (p_cap->halAutoSleepSupport) {
175b7d5e03cSMatthew Dillon         beacontimeout = (BEACON_TIMEOUT_VAL << 3);
176b7d5e03cSMatthew Dillon     } else {
177b7d5e03cSMatthew Dillon         /*
178b7d5e03cSMatthew Dillon          * Use a very small value to make sure the timeout occurs before
179b7d5e03cSMatthew Dillon          * the TBTT.  In this case the chip will not go back to sleep
180b7d5e03cSMatthew Dillon          * automatically, instead it will wait for the SW to explicitly
181b7d5e03cSMatthew Dillon          * set it to that mode.
182b7d5e03cSMatthew Dillon          */
183b7d5e03cSMatthew Dillon         beacontimeout = MIN_BEACON_TIMEOUT_VAL;
184b7d5e03cSMatthew Dillon     }
185b7d5e03cSMatthew Dillon 
186b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_SLEEP2,
187b7d5e03cSMatthew Dillon         SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT));
188b7d5e03cSMatthew Dillon 
189b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval));
190b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod));
191b7d5e03cSMatthew Dillon 
192b7d5e03cSMatthew Dillon     /* clear HOST AP related timers first */
193b7d5e03cSMatthew Dillon     OS_REG_CLR_BIT(ah, AR_TIMER_MODE, (AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN));
194b7d5e03cSMatthew Dillon 
195b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN
196b7d5e03cSMatthew Dillon                     | AR_DTIM_TIMER_EN);
197b7d5e03cSMatthew Dillon 
198b7d5e03cSMatthew Dillon     /* TSF out of range threshold */
199b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold);
200b7d5e03cSMatthew Dillon 
201b7d5e03cSMatthew Dillon #undef CAB_TIMEOUT_VAL
202b7d5e03cSMatthew Dillon #undef BEACON_TIMEOUT_VAL
203b7d5e03cSMatthew Dillon #undef SLEEP_SLOP
204b7d5e03cSMatthew Dillon }
205