xref: /freebsd/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c (revision b9f654b163bce26de79705e77b872427c9f2afa1)
1 /*-
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
5  * Copyright (c) 2002-2004 Atheros Communications, Inc.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * $FreeBSD$
20  */
21 #include "opt_ah.h"
22 
23 #include "ah.h"
24 #include "ah_internal.h"
25 #include "ah_desc.h"
26 
27 #include "ar5210/ar5210.h"
28 #include "ar5210/ar5210reg.h"
29 #include "ar5210/ar5210desc.h"
30 
31 /*
32  * Return the hardware NextTBTT in TSF
33  */
34 uint64_t
35 ar5210GetNextTBTT(struct ath_hal *ah)
36 {
37 #define TU_TO_TSF(_tu)	(((uint64_t)(_tu)) << 10)
38 	return TU_TO_TSF(OS_REG_READ(ah, AR_TIMER0));
39 #undef TU_TO_TSF
40 }
41 
42 /*
43  * Initialize all of the hardware registers used to send beacons.
44  */
45 void
46 ar5210SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt)
47 {
48 
49 	OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt);
50 	OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba);
51 	OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba);
52 	OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim);
53 	/*
54 	 * Set the Beacon register after setting all timers.
55 	 */
56 	OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval);
57 }
58 
59 /*
60  * Legacy api to Initialize all of the beacon registers.
61  */
62 void
63 ar5210BeaconInit(struct ath_hal *ah,
64 	uint32_t next_beacon, uint32_t beacon_period)
65 {
66 	HAL_BEACON_TIMERS bt;
67 
68 	bt.bt_nexttbtt = next_beacon;
69 
70 	if (AH_PRIVATE(ah)->ah_opmode != HAL_M_STA) {
71 		bt.bt_nextdba = (next_beacon -
72 			ah->ah_config.ah_dma_beacon_response_time) << 3; /* 1/8 TU */
73 		bt.bt_nextswba = (next_beacon -
74             ah->ah_config.ah_sw_beacon_response_time) << 3;	/* 1/8 TU */
75 		/*
76 		 * The SWBA interrupt is not used for beacons in ad hoc mode
77 		 * as we don't yet support ATIMs. So since the beacon never
78 		 * changes, the beacon descriptor is set up once and read
79 		 * into a special HW buffer, from which it will be
80 		 * automagically retrieved at each DMA Beacon Alert (DBA).
81 		 */
82 
83 		/* Set the ATIM window */
84 		bt.bt_nextatim = next_beacon + 0;	/* NB: no ATIMs */
85 	} else {
86 		bt.bt_nextdba = ~0;
87 		bt.bt_nextswba = ~0;
88 		bt.bt_nextatim = 1;
89 	}
90 	bt.bt_intval = beacon_period &
91 		(AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN);
92 	ar5210SetBeaconTimers(ah, &bt);
93 }
94 
95 void
96 ar5210ResetStaBeaconTimers(struct ath_hal *ah)
97 {
98 	uint32_t val;
99 
100 	OS_REG_WRITE(ah, AR_TIMER0, 0);		/* no beacons */
101 	val = OS_REG_READ(ah, AR_STA_ID1);
102 	val |= AR_STA_ID1_NO_PSPOLL;		/* XXX */
103 	/* tell the h/w that the associated AP is not PCF capable */
104 	OS_REG_WRITE(ah, AR_STA_ID1,
105 		val & ~(AR_STA_ID1_DEFAULT_ANTENNA | AR_STA_ID1_PCF));
106 	OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_PERIOD);
107 }
108 
109 /*
110  * Set all the beacon related bits on the h/w for stations
111  * i.e. initializes the corresponding h/w timers;
112  * also tells the h/w whether to anticipate PCF beacons
113  *
114  * dtim_count and cfp_count from the current beacon - their current
115  * values aren't necessarily maintained in the device struct
116  */
117 void
118 ar5210SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs)
119 {
120 	struct ath_hal_5210 *ahp = AH5210(ah);
121 
122 	HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: setting beacon timers\n", __func__);
123 
124 	HALASSERT(bs->bs_intval != 0);
125 	/* if the AP will do PCF */
126 	if (bs->bs_cfpmaxduration != 0) {
127 		/* tell the h/w that the associated AP is PCF capable */
128 		OS_REG_WRITE(ah, AR_STA_ID1,
129 			(OS_REG_READ(ah, AR_STA_ID1) &~ AR_STA_ID1_DEFAULT_ANTENNA)
130 			| AR_STA_ID1_PCF);
131 
132 		/* set CFP_PERIOD(1.024ms) register */
133 		OS_REG_WRITE(ah, AR_CFP_PERIOD, bs->bs_cfpperiod);
134 
135 		/* set CFP_DUR(1.024ms) register to max cfp duration */
136 		OS_REG_WRITE(ah, AR_CFP_DUR, bs->bs_cfpmaxduration);
137 
138 		/* set TIMER2(128us) to anticipated time of next CFP */
139 		OS_REG_WRITE(ah, AR_TIMER2, bs->bs_cfpnext << 3);
140 	} else {
141 		/* tell the h/w that the associated AP is not PCF capable */
142 		OS_REG_WRITE(ah, AR_STA_ID1,
143 			OS_REG_READ(ah, AR_STA_ID1) &~ (AR_STA_ID1_DEFAULT_ANTENNA | AR_STA_ID1_PCF));
144 	}
145 
146 	/*
147 	 * Set TIMER0(1.024ms) to the anticipated time of the next beacon.
148 	 */
149 	OS_REG_WRITE(ah, AR_TIMER0, bs->bs_nexttbtt);
150 
151 	/*
152 	 * Start the beacon timers by setting the BEACON register
153 	 * to the beacon interval; also write the tim offset which
154 	 * we should know by now.  The code, in ar5211WriteAssocid,
155 	 * also sets the tim offset once the AID is known which can
156 	 * be left as such for now.
157 	 */
158 	OS_REG_WRITE(ah, AR_BEACON,
159 		(OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_PERIOD|AR_BEACON_TIM))
160 		| SM(bs->bs_intval, AR_BEACON_PERIOD)
161 		| SM(bs->bs_timoffset ? bs->bs_timoffset + 4 : 0, AR_BEACON_TIM)
162 	);
163 
164 	/*
165 	 * Configure the BMISS interrupt.  Note that we
166 	 * assume the caller blocks interrupts while enabling
167 	 * the threshold.
168 	 */
169 
170 	/*
171 	 * Interrupt works only on Crete.
172 	 */
173 	if (AH_PRIVATE(ah)->ah_macRev < AR_SREV_CRETE)
174 		return;
175 	/*
176 	 * Counter is only 3-bits.
177 	 * Count of 0 with BMISS interrupt enabled will hang the system
178 	 * with too many interrupts
179 	 */
180 	if (AH_PRIVATE(ah)->ah_macRev >= AR_SREV_CRETE &&
181 	    (bs->bs_bmissthreshold&7) == 0) {
182 #ifdef AH_DEBUG
183 		ath_hal_printf(ah, "%s: invalid beacon miss threshold %u\n",
184 			__func__, bs->bs_bmissthreshold);
185 #endif
186 		return;
187 	}
188 #define	BMISS_MAX	(AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S)
189 	/*
190 	 * Configure the BMISS interrupt.  Note that we
191 	 * assume the caller blocks interrupts while enabling
192 	 * the threshold.
193 	 *
194 	 * NB: the beacon miss count field is only 3 bits which
195 	 *     is much smaller than what's found on later parts;
196 	 *     clamp overflow values as a safeguard.
197 	 */
198 	ahp->ah_rssiThr = (ahp->ah_rssiThr &~ AR_RSSI_THR_BM_THR)
199 			| SM(bs->bs_bmissthreshold > BMISS_MAX ?
200 				BMISS_MAX : bs->bs_bmissthreshold,
201 			     AR_RSSI_THR_BM_THR);
202 	OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
203 #undef BMISS_MAX
204 }
205