1 | /* |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
3 | * Copyright (c) 2002-2008 Atheros Communications, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | * $Id: ar5416_beacon.c,v 1.2 2013/09/12 12:08:49 martin Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | |
24 | #include "ar5416/ar5416.h" |
25 | #include "ar5416/ar5416reg.h" |
26 | #include "ar5416/ar5416phy.h" |
27 | |
28 | #define TU_TO_USEC(_tu) ((_tu) << 10) |
29 | |
30 | /* |
31 | * Initialize all of the hardware registers used to |
32 | * send beacons. Note that for station operation the |
33 | * driver calls ar5416SetStaBeaconTimers instead. |
34 | */ |
35 | void |
36 | ar5416SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) |
37 | { |
38 | uint32_t bperiod; |
39 | |
40 | OS_REG_WRITE(ah, AR_NEXT_TBTT, TU_TO_USEC(bt->bt_nexttbtt)); |
41 | OS_REG_WRITE(ah, AR_NEXT_DBA, TU_TO_USEC(bt->bt_nextdba) >> 3); |
42 | OS_REG_WRITE(ah, AR_NEXT_SWBA, TU_TO_USEC(bt->bt_nextswba) >> 3); |
43 | OS_REG_WRITE(ah, AR_NEXT_NDP, TU_TO_USEC(bt->bt_nextatim)); |
44 | |
45 | bperiod = TU_TO_USEC(bt->bt_intval & HAL_BEACON_PERIOD); |
46 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, bperiod); |
47 | OS_REG_WRITE(ah, AR_DBA_PERIOD, bperiod); |
48 | OS_REG_WRITE(ah, AR_SWBA_PERIOD, bperiod); |
49 | OS_REG_WRITE(ah, AR_NDP_PERIOD, bperiod); |
50 | |
51 | /* |
52 | * Reset TSF if required. |
53 | */ |
54 | if (bt->bt_intval & AR_BEACON_RESET_TSF) |
55 | ar5416ResetTsf(ah); |
56 | |
57 | /* enable timers */ |
58 | /* NB: flags == 0 handled specially for backwards compatibility */ |
59 | OS_REG_SET_BIT(ah, AR_TIMER_MODE, |
60 | bt->bt_flags != 0 ? bt->bt_flags : |
61 | AR_TIMER_MODE_TBTT | AR_TIMER_MODE_DBA | AR_TIMER_MODE_SWBA); |
62 | } |
63 | |
64 | /* |
65 | * Initializes all of the hardware registers used to |
66 | * send beacons. Note that for station operation the |
67 | * driver calls ar5212SetStaBeaconTimers instead. |
68 | */ |
69 | void |
70 | ar5416BeaconInit(struct ath_hal *ah, |
71 | uint32_t next_beacon, uint32_t beacon_period) |
72 | { |
73 | HAL_BEACON_TIMERS bt; |
74 | |
75 | bt.bt_nexttbtt = next_beacon; |
76 | /* |
77 | * TIMER1: in AP/adhoc mode this controls the DMA beacon |
78 | * alert timer; otherwise it controls the next wakeup time. |
79 | * TIMER2: in AP mode, it controls the SBA beacon alert |
80 | * interrupt; otherwise it sets the start of the next CFP. |
81 | */ |
82 | bt.bt_flags = 0; |
83 | switch (AH_PRIVATE(ah)->ah_opmode) { |
84 | case HAL_M_STA: |
85 | case HAL_M_MONITOR: |
86 | bt.bt_nextdba = 0xffff; |
87 | bt.bt_nextswba = 0x7ffff; |
88 | bt.bt_flags |= AR_TIMER_MODE_TBTT; |
89 | break; |
90 | case HAL_M_IBSS: |
91 | OS_REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ATIM_TXPOLICY); |
92 | bt.bt_flags |= AR_TIMER_MODE_NDP; |
93 | /* fall thru... */ |
94 | case HAL_M_HOSTAP: |
95 | bt.bt_nextdba = (next_beacon - |
96 | ath_hal_dma_beacon_response_time) << 3; /* 1/8 TU */ |
97 | bt.bt_nextswba = (next_beacon - |
98 | ath_hal_sw_beacon_response_time) << 3; /* 1/8 TU */ |
99 | bt.bt_flags |= AR_TIMER_MODE_TBTT |
100 | | AR_TIMER_MODE_DBA |
101 | | AR_TIMER_MODE_SWBA; |
102 | break; |
103 | } |
104 | /* |
105 | * Set the ATIM window |
106 | * Our hardware does not support an ATIM window of 0 |
107 | * (beacons will not work). If the ATIM windows is 0, |
108 | * force it to 1. |
109 | */ |
110 | bt.bt_nextatim = next_beacon + 1; |
111 | bt.bt_intval = beacon_period & |
112 | (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN); |
113 | ar5416SetBeaconTimers(ah, &bt); |
114 | } |
115 | |
116 | #define AR_BEACON_PERIOD_MAX 0xffff |
117 | |
118 | void |
119 | ar5416ResetStaBeaconTimers(struct ath_hal *ah) |
120 | { |
121 | uint32_t val; |
122 | |
123 | OS_REG_WRITE(ah, AR_NEXT_TBTT, 0); /* no beacons */ |
124 | val = OS_REG_READ(ah, AR_STA_ID1); |
125 | val |= AR_STA_ID1_PWR_SAV; /* XXX */ |
126 | /* tell the h/w that the associated AP is not PCF capable */ |
127 | OS_REG_WRITE(ah, AR_STA_ID1, |
128 | val & ~(AR_STA_ID1_USE_DEFANT | AR_STA_ID1_PCF)); |
129 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, AR_BEACON_PERIOD_MAX); |
130 | OS_REG_WRITE(ah, AR_DBA_PERIOD, AR_BEACON_PERIOD_MAX); |
131 | } |
132 | |
133 | /* |
134 | * Set all the beacon related bits on the h/w for stations |
135 | * i.e. initializes the corresponding h/w timers; |
136 | * also tells the h/w whether to anticipate PCF beacons |
137 | */ |
138 | void |
139 | ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) |
140 | { |
141 | uint32_t nextTbtt,beaconintval, dtimperiod; |
142 | |
143 | HALASSERT(bs->bs_intval != 0); |
144 | |
145 | /* NB: no cfp setting since h/w automatically takes care */ |
146 | |
147 | OS_REG_WRITE(ah, AR_NEXT_TBTT, bs->bs_nexttbtt); |
148 | |
149 | /* |
150 | * Start the beacon timers by setting the BEACON register |
151 | * to the beacon interval; no need to write tim offset since |
152 | * h/w parses IEs. |
153 | */ |
154 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, |
155 | TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); |
156 | OS_REG_WRITE(ah, AR_DBA_PERIOD, |
157 | TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); |
158 | |
159 | /* |
160 | * Configure the BMISS interrupt. Note that we |
161 | * assume the caller blocks interrupts while enabling |
162 | * the threshold. |
163 | */ |
164 | HALASSERT(bs->bs_bmissthreshold <= |
165 | (AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S)); |
166 | OS_REG_RMW_FIELD(ah, AR_RSSI_THR, |
167 | AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); |
168 | |
169 | /* |
170 | * Program the sleep registers to correlate with the beacon setup. |
171 | */ |
172 | |
173 | /* |
174 | * Oahu beacons timers on the station were used for power |
175 | * save operation (waking up in anticipation of a beacon) |
176 | * and any CFP function; Venice does sleep/power-save timers |
177 | * differently - so this is the right place to set them up; |
178 | * don't think the beacon timers are used by venice sta hw |
179 | * for any useful purpose anymore |
180 | * Setup venice's sleep related timers |
181 | * Current implementation assumes sw processing of beacons - |
182 | * assuming an interrupt is generated every beacon which |
183 | * causes the hardware to become awake until the sw tells |
184 | * it to go to sleep again; beacon timeout is to allow for |
185 | * beacon jitter; cab timeout is max time to wait for cab |
186 | * after seeing the last DTIM or MORE CAB bit |
187 | */ |
188 | #define CAB_TIMEOUT_VAL 10 /* in TU */ |
189 | #define BEACON_TIMEOUT_VAL 10 /* in TU */ |
190 | #define SLEEP_SLOP 3 /* in TU */ |
191 | |
192 | /* |
193 | * For max powersave mode we may want to sleep for longer than a |
194 | * beacon period and not want to receive all beacons; modify the |
195 | * timers accordingly; make sure to align the next TIM to the |
196 | * next DTIM if we decide to wake for DTIMs only |
197 | */ |
198 | beaconintval = bs->bs_intval & HAL_BEACON_PERIOD; |
199 | HALASSERT(beaconintval != 0); |
200 | if (bs->bs_sleepduration > beaconintval) { |
201 | HALASSERT(roundup(bs->bs_sleepduration, beaconintval) == |
202 | bs->bs_sleepduration); |
203 | beaconintval = bs->bs_sleepduration; |
204 | } |
205 | dtimperiod = bs->bs_dtimperiod; |
206 | if (bs->bs_sleepduration > dtimperiod) { |
207 | HALASSERT(dtimperiod == 0 || |
208 | roundup(bs->bs_sleepduration, dtimperiod) == |
209 | bs->bs_sleepduration); |
210 | dtimperiod = bs->bs_sleepduration; |
211 | } |
212 | HALASSERT(beaconintval <= dtimperiod); |
213 | if (beaconintval == dtimperiod) |
214 | nextTbtt = bs->bs_nextdtim; |
215 | else |
216 | nextTbtt = bs->bs_nexttbtt; |
217 | |
218 | OS_REG_WRITE(ah, AR_NEXT_DTIM, |
219 | TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); |
220 | OS_REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); |
221 | |
222 | /* cab timeout is now in 1/8 TU */ |
223 | OS_REG_WRITE(ah, AR_SLEEP1, |
224 | SM((CAB_TIMEOUT_VAL << 3), AR5416_SLEEP1_CAB_TIMEOUT) |
225 | | AR_SLEEP1_ASSUME_DTIM); |
226 | /* beacon timeout is now in 1/8 TU */ |
227 | OS_REG_WRITE(ah, AR_SLEEP2, |
228 | SM((BEACON_TIMEOUT_VAL << 3), AR5416_SLEEP2_BEACON_TIMEOUT)); |
229 | |
230 | OS_REG_WRITE(ah, AR_TIM_PERIOD, beaconintval); |
231 | OS_REG_WRITE(ah, AR_DTIM_PERIOD, dtimperiod); |
232 | OS_REG_SET_BIT(ah, AR_TIMER_MODE, |
233 | AR_TIMER_MODE_TBTT | AR_TIMER_MODE_TIM | AR_TIMER_MODE_DTIM); |
234 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n" , |
235 | __func__, bs->bs_nextdtim); |
236 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n" , |
237 | __func__, nextTbtt); |
238 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: beacon period %d\n" , |
239 | __func__, beaconintval); |
240 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: DTIM period %d\n" , |
241 | __func__, dtimperiod); |
242 | #undef CAB_TIMEOUT_VAL |
243 | #undef BEACON_TIMEOUT_VAL |
244 | #undef SLEEP_SLOP |
245 | } |
246 | |