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