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: ar5212_beacon.c,v 1.1.1.1 2008/12/11 04:46:40 alc Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | |
24 | #include "ar5212/ar5212.h" |
25 | #include "ar5212/ar5212reg.h" |
26 | #include "ar5212/ar5212desc.h" |
27 | |
28 | /* |
29 | * Initialize all of the hardware registers used to |
30 | * send beacons. Note that for station operation the |
31 | * driver calls ar5212SetStaBeaconTimers instead. |
32 | */ |
33 | void |
34 | ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) |
35 | { |
36 | |
37 | OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt); |
38 | OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba); |
39 | OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba); |
40 | OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim); |
41 | /* |
42 | * Set the Beacon register after setting all timers. |
43 | */ |
44 | if (bt->bt_intval & AR_BEACON_RESET_TSF) { |
45 | /* |
46 | * When resetting the TSF, |
47 | * write twice to the corresponding register; each |
48 | * write to the RESET_TSF bit toggles the internal |
49 | * signal to cause a reset of the TSF - but if the signal |
50 | * is left high, it will reset the TSF on the next |
51 | * chip reset also! writing the bit an even number |
52 | * of times fixes this issue |
53 | */ |
54 | OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_RESET_TSF); |
55 | } |
56 | OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval); |
57 | } |
58 | |
59 | /* |
60 | * Old api for setting up beacon timer registers when |
61 | * operating in !station mode. Note the fixed constants |
62 | * adjusting the DBA and SWBA timers and the fixed ATIM |
63 | * window. |
64 | */ |
65 | void |
66 | ar5212BeaconInit(struct ath_hal *ah, |
67 | uint32_t next_beacon, uint32_t beacon_period) |
68 | { |
69 | HAL_BEACON_TIMERS bt; |
70 | |
71 | bt.bt_nexttbtt = next_beacon; |
72 | /* |
73 | * TIMER1: in AP/adhoc mode this controls the DMA beacon |
74 | * alert timer; otherwise it controls the next wakeup time. |
75 | * TIMER2: in AP mode, it controls the SBA beacon alert |
76 | * interrupt; otherwise it sets the start of the next CFP. |
77 | */ |
78 | switch (AH_PRIVATE(ah)->ah_opmode) { |
79 | case HAL_M_STA: |
80 | case HAL_M_MONITOR: |
81 | bt.bt_nextdba = 0xffff; |
82 | bt.bt_nextswba = 0x7ffff; |
83 | break; |
84 | case HAL_M_HOSTAP: |
85 | case HAL_M_IBSS: |
86 | bt.bt_nextdba = (next_beacon - |
87 | ath_hal_dma_beacon_response_time) << 3; /* 1/8 TU */ |
88 | bt.bt_nextswba = (next_beacon - |
89 | ath_hal_sw_beacon_response_time) << 3; /* 1/8 TU */ |
90 | break; |
91 | } |
92 | /* |
93 | * Set the ATIM window |
94 | * Our hardware does not support an ATIM window of 0 |
95 | * (beacons will not work). If the ATIM windows is 0, |
96 | * force it to 1. |
97 | */ |
98 | bt.bt_nextatim = next_beacon + 1; |
99 | bt.bt_intval = beacon_period & |
100 | (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN); |
101 | ar5212SetBeaconTimers(ah, &bt); |
102 | } |
103 | |
104 | void |
105 | ar5212ResetStaBeaconTimers(struct ath_hal *ah) |
106 | { |
107 | uint32_t val; |
108 | |
109 | OS_REG_WRITE(ah, AR_TIMER0, 0); /* no beacons */ |
110 | val = OS_REG_READ(ah, AR_STA_ID1); |
111 | val |= AR_STA_ID1_PWR_SAV; /* XXX */ |
112 | /* tell the h/w that the associated AP is not PCF capable */ |
113 | OS_REG_WRITE(ah, AR_STA_ID1, |
114 | val & ~(AR_STA_ID1_USE_DEFANT | AR_STA_ID1_PCF)); |
115 | OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_PERIOD); |
116 | } |
117 | |
118 | /* |
119 | * Set all the beacon related bits on the h/w for stations |
120 | * i.e. initializes the corresponding h/w timers; |
121 | * also tells the h/w whether to anticipate PCF beacons |
122 | */ |
123 | void |
124 | ar5212SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) |
125 | { |
126 | struct ath_hal_5212 *ahp = AH5212(ah); |
127 | uint32_t nextTbtt, nextdtim,beaconintval, dtimperiod; |
128 | |
129 | HALASSERT(bs->bs_intval != 0); |
130 | /* if the AP will do PCF */ |
131 | if (bs->bs_cfpmaxduration != 0) { |
132 | /* tell the h/w that the associated AP is PCF capable */ |
133 | OS_REG_WRITE(ah, AR_STA_ID1, |
134 | OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PCF); |
135 | |
136 | /* set CFP_PERIOD(1.024ms) register */ |
137 | OS_REG_WRITE(ah, AR_CFP_PERIOD, bs->bs_cfpperiod); |
138 | |
139 | /* set CFP_DUR(1.024ms) register to max cfp duration */ |
140 | OS_REG_WRITE(ah, AR_CFP_DUR, bs->bs_cfpmaxduration); |
141 | |
142 | /* set TIMER2(128us) to anticipated time of next CFP */ |
143 | OS_REG_WRITE(ah, AR_TIMER2, bs->bs_cfpnext << 3); |
144 | } else { |
145 | /* tell the h/w that the associated AP is not PCF capable */ |
146 | OS_REG_WRITE(ah, AR_STA_ID1, |
147 | OS_REG_READ(ah, AR_STA_ID1) &~ AR_STA_ID1_PCF); |
148 | } |
149 | |
150 | /* |
151 | * Set TIMER0(1.024ms) to the anticipated time of the next beacon. |
152 | */ |
153 | OS_REG_WRITE(ah, AR_TIMER0, bs->bs_nexttbtt); |
154 | |
155 | /* |
156 | * Start the beacon timers by setting the BEACON register |
157 | * to the beacon interval; also write the tim offset which |
158 | * we should know by now. The code, in ar5211WriteAssocid, |
159 | * also sets the tim offset once the AID is known which can |
160 | * be left as such for now. |
161 | */ |
162 | OS_REG_WRITE(ah, AR_BEACON, |
163 | (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_PERIOD|AR_BEACON_TIM)) |
164 | | SM(bs->bs_intval, AR_BEACON_PERIOD) |
165 | | SM(bs->bs_timoffset ? bs->bs_timoffset + 4 : 0, AR_BEACON_TIM) |
166 | ); |
167 | |
168 | /* |
169 | * Configure the BMISS interrupt. Note that we |
170 | * assume the caller blocks interrupts while enabling |
171 | * the threshold. |
172 | */ |
173 | HALASSERT(bs->bs_bmissthreshold <= MS(0xffffffff, AR_RSSI_THR_BM_THR)); |
174 | ahp->ah_rssiThr = (ahp->ah_rssiThr &~ AR_RSSI_THR_BM_THR) |
175 | | SM(bs->bs_bmissthreshold, AR_RSSI_THR_BM_THR); |
176 | OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); |
177 | |
178 | /* |
179 | * Program the sleep registers to correlate with the beacon setup. |
180 | */ |
181 | |
182 | /* |
183 | * Oahu beacons timers on the station were used for power |
184 | * save operation (waking up in anticipation of a beacon) |
185 | * and any CFP function; Venice does sleep/power-save timers |
186 | * differently - so this is the right place to set them up; |
187 | * don't think the beacon timers are used by venice sta hw |
188 | * for any useful purpose anymore |
189 | * Setup venice's sleep related timers |
190 | * Current implementation assumes sw processing of beacons - |
191 | * assuming an interrupt is generated every beacon which |
192 | * causes the hardware to become awake until the sw tells |
193 | * it to go to sleep again; beacon timeout is to allow for |
194 | * beacon jitter; cab timeout is max time to wait for cab |
195 | * after seeing the last DTIM or MORE CAB bit |
196 | */ |
197 | #define CAB_TIMEOUT_VAL 10 /* in TU */ |
198 | #define BEACON_TIMEOUT_VAL 10 /* in TU */ |
199 | #define SLEEP_SLOP 3 /* in TU */ |
200 | |
201 | /* |
202 | * For max powersave mode we may want to sleep for longer than a |
203 | * beacon period and not want to receive all beacons; modify the |
204 | * timers accordingly; make sure to align the next TIM to the |
205 | * next DTIM if we decide to wake for DTIMs only |
206 | */ |
207 | beaconintval = bs->bs_intval & HAL_BEACON_PERIOD; |
208 | HALASSERT(beaconintval != 0); |
209 | if (bs->bs_sleepduration > beaconintval) { |
210 | HALASSERT(roundup(bs->bs_sleepduration, beaconintval) == |
211 | bs->bs_sleepduration); |
212 | beaconintval = bs->bs_sleepduration; |
213 | } |
214 | dtimperiod = bs->bs_dtimperiod; |
215 | if (bs->bs_sleepduration > dtimperiod) { |
216 | HALASSERT(dtimperiod == 0 || |
217 | roundup(bs->bs_sleepduration, dtimperiod) == |
218 | bs->bs_sleepduration); |
219 | dtimperiod = bs->bs_sleepduration; |
220 | } |
221 | HALASSERT(beaconintval <= dtimperiod); |
222 | if (beaconintval == dtimperiod) |
223 | nextTbtt = bs->bs_nextdtim; |
224 | else |
225 | nextTbtt = bs->bs_nexttbtt; |
226 | nextdtim = bs->bs_nextdtim; |
227 | |
228 | OS_REG_WRITE(ah, AR_SLEEP1, |
229 | SM((nextdtim - SLEEP_SLOP) << 3, AR_SLEEP1_NEXT_DTIM) |
230 | | SM(CAB_TIMEOUT_VAL, AR_SLEEP1_CAB_TIMEOUT) |
231 | | AR_SLEEP1_ASSUME_DTIM |
232 | | AR_SLEEP1_ENH_SLEEP_ENA |
233 | ); |
234 | OS_REG_WRITE(ah, AR_SLEEP2, |
235 | SM((nextTbtt - SLEEP_SLOP) << 3, AR_SLEEP2_NEXT_TIM) |
236 | | SM(BEACON_TIMEOUT_VAL, AR_SLEEP2_BEACON_TIMEOUT) |
237 | ); |
238 | OS_REG_WRITE(ah, AR_SLEEP3, |
239 | SM(beaconintval, AR_SLEEP3_TIM_PERIOD) |
240 | | SM(dtimperiod, AR_SLEEP3_DTIM_PERIOD) |
241 | ); |
242 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n" , |
243 | __func__, bs->bs_nextdtim); |
244 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n" , |
245 | __func__, nextTbtt); |
246 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: beacon period %d\n" , |
247 | __func__, beaconintval); |
248 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: DTIM period %d\n" , |
249 | __func__, dtimperiod); |
250 | #undef CAB_TIMEOUT_VAL |
251 | #undef BEACON_TIMEOUT_VAL |
252 | #undef SLEEP_SLOP |
253 | } |
254 | |