1 /*-
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2010-2011 Atheros Communications, Inc.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "opt_ah.h"
19
20 #include "ah.h"
21 #include "ah_internal.h"
22 #include "ah_devid.h"
23 #include "ah_desc.h" /* NB: for HAL_PHYERR* */
24
25 #include "ar5416/ar5416.h"
26 #include "ar5416/ar5416reg.h"
27 #include "ar5416/ar5416phy.h"
28
29 #include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */
30
31 /*
32 * These are default parameters for the AR5416 and
33 * later 802.11n NICs. They simply enable some
34 * radar pulse event generation.
35 *
36 * These are very likely not valid for the AR5212 era
37 * NICs.
38 *
39 * Since these define signal sizing and threshold
40 * parameters, they may need changing based on the
41 * specific antenna and receive amplifier
42 * configuration.
43 */
44 #define AR5416_DFS_FIRPWR -33
45 #define AR5416_DFS_RRSSI 20
46 #define AR5416_DFS_HEIGHT 10
47 #define AR5416_DFS_PRSSI 15
48 #define AR5416_DFS_INBAND 15
49 #define AR5416_DFS_RELPWR 8
50 #define AR5416_DFS_RELSTEP 12
51 #define AR5416_DFS_MAXLEN 255
52
53 HAL_BOOL
ar5416GetDfsDefaultThresh(struct ath_hal * ah,HAL_PHYERR_PARAM * pe)54 ar5416GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
55 {
56
57 /*
58 * These are general examples of the parameter values
59 * to use when configuring radar pulse detection for
60 * the AR5416, AR91xx, AR92xx NICs. They are only
61 * for testing and do require tuning depending upon the
62 * hardware and deployment specifics.
63 */
64 pe->pe_firpwr = AR5416_DFS_FIRPWR;
65 pe->pe_rrssi = AR5416_DFS_RRSSI;
66 pe->pe_height = AR5416_DFS_HEIGHT;
67 pe->pe_prssi = AR5416_DFS_PRSSI;
68 pe->pe_inband = AR5416_DFS_INBAND;
69 pe->pe_relpwr = AR5416_DFS_RELPWR;
70 pe->pe_relstep = AR5416_DFS_RELSTEP;
71 pe->pe_maxlen = AR5416_DFS_MAXLEN;
72
73 return (AH_TRUE);
74 }
75
76 /*
77 * Get the radar parameter values and return them in the pe
78 * structure
79 */
80 void
ar5416GetDfsThresh(struct ath_hal * ah,HAL_PHYERR_PARAM * pe)81 ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
82 {
83 uint32_t val, temp;
84
85 val = OS_REG_READ(ah, AR_PHY_RADAR_0);
86
87 temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
88 temp |= 0xFFFFFF80;
89 pe->pe_firpwr = temp;
90 pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
91 pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT);
92 pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
93 pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
94
95 /* RADAR_1 values */
96 val = OS_REG_READ(ah, AR_PHY_RADAR_1);
97 pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH);
98 pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH);
99 pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN);
100
101 pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) &
102 AR_PHY_RADAR_EXT_ENA);
103
104 pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
105 AR_PHY_RADAR_1_USE_FIR128);
106 pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
107 AR_PHY_RADAR_1_BLOCK_CHECK);
108 pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
109 AR_PHY_RADAR_1_MAX_RRSSI);
110 pe->pe_enabled = !!
111 (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA);
112 pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
113 AR_PHY_RADAR_1_RELPWR_ENA);
114 pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
115 AR_PHY_RADAR_1_RELSTEP_CHECK);
116 }
117
118 /*
119 * Enable radar detection and set the radar parameters per the
120 * values in pe
121 */
122 void
ar5416EnableDfs(struct ath_hal * ah,HAL_PHYERR_PARAM * pe)123 ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
124 {
125 uint32_t val;
126
127 val = OS_REG_READ(ah, AR_PHY_RADAR_0);
128
129 if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
130 val &= ~AR_PHY_RADAR_0_FIRPWR;
131 val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
132 }
133 if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
134 val &= ~AR_PHY_RADAR_0_RRSSI;
135 val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
136 }
137 if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
138 val &= ~AR_PHY_RADAR_0_HEIGHT;
139 val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
140 }
141 if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
142 val &= ~AR_PHY_RADAR_0_PRSSI;
143 val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
144 }
145 if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
146 val &= ~AR_PHY_RADAR_0_INBAND;
147 val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
148 }
149
150 /*Enable FFT data*/
151 val |= AR_PHY_RADAR_0_FFT_ENA;
152 OS_REG_WRITE(ah, AR_PHY_RADAR_0, val);
153
154 /* Implicitly enable */
155 if (pe->pe_enabled == 1)
156 OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
157 else if (pe->pe_enabled == 0)
158 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
159
160 if (pe->pe_usefir128 == 1)
161 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
162 else if (pe->pe_usefir128 == 0)
163 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
164
165 if (pe->pe_enmaxrssi == 1)
166 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
167 else if (pe->pe_enmaxrssi == 0)
168 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
169
170 if (pe->pe_blockradar == 1)
171 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
172 else if (pe->pe_blockradar == 0)
173 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
174
175 if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) {
176 val = OS_REG_READ(ah, AR_PHY_RADAR_1);
177 val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH;
178 val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH);
179 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
180 }
181 if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) {
182 val = OS_REG_READ(ah, AR_PHY_RADAR_1);
183 val &= ~AR_PHY_RADAR_1_RELPWR_THRESH;
184 val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH);
185 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
186 }
187
188 if (pe->pe_en_relstep_check == 1)
189 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
190 AR_PHY_RADAR_1_RELSTEP_CHECK);
191 else if (pe->pe_en_relstep_check == 0)
192 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
193 AR_PHY_RADAR_1_RELSTEP_CHECK);
194
195 if (pe->pe_enrelpwr == 1)
196 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
197 AR_PHY_RADAR_1_RELPWR_ENA);
198 else if (pe->pe_enrelpwr == 0)
199 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
200 AR_PHY_RADAR_1_RELPWR_ENA);
201
202 if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) {
203 val = OS_REG_READ(ah, AR_PHY_RADAR_1);
204 val &= ~AR_PHY_RADAR_1_MAXLEN;
205 val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN);
206 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
207 }
208
209 /*
210 * Enable HT/40 if the upper layer asks;
211 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS
212 * is available.
213 */
214 if (pe->pe_extchannel == 1)
215 OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
216 else if (pe->pe_extchannel == 0)
217 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
218 }
219
220 /*
221 * Extract the radar event information from the given phy error.
222 *
223 * Returns AH_TRUE if the phy error was actually a phy error,
224 * AH_FALSE if the phy error wasn't a phy error.
225 */
226
227 /* Flags for pulse_bw_info */
228 #define PRI_CH_RADAR_FOUND 0x01
229 #define EXT_CH_RADAR_FOUND 0x02
230 #define EXT_CH_RADAR_EARLY_FOUND 0x04
231
232 HAL_BOOL
ar5416ProcessRadarEvent(struct ath_hal * ah,struct ath_rx_status * rxs,uint64_t fulltsf,const char * buf,HAL_DFS_EVENT * event)233 ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
234 uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
235 {
236 HAL_BOOL doDfsExtCh;
237 HAL_BOOL doDfsEnhanced;
238 HAL_BOOL doDfsCombinedRssi;
239
240 uint8_t rssi = 0, ext_rssi = 0;
241 uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0;
242 uint32_t dur = 0;
243 int pri_found = 1, ext_found = 0;
244 int early_ext = 0;
245 int is_dc = 0;
246 uint16_t datalen; /* length from the RX status field */
247
248 /* Check whether the given phy error is a radar event */
249 if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) &&
250 (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) {
251 return AH_FALSE;
252 }
253
254 /* Grab copies of the capabilities; just to make the code clearer */
255 doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport;
256 doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport;
257 doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi;
258
259 datalen = rxs->rs_datalen;
260
261 /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */
262 if (doDfsCombinedRssi)
263 rssi = (uint8_t) rxs->rs_rssi;
264 else
265 rssi = (uint8_t) rxs->rs_rssi_ctl[0];
266
267 /* Set this; but only use it if doDfsExtCh is set */
268 ext_rssi = (uint8_t) rxs->rs_rssi_ext[0];
269
270 /* Cap it at 0 if the RSSI is a negative number */
271 if (rssi & 0x80)
272 rssi = 0;
273
274 if (ext_rssi & 0x80)
275 ext_rssi = 0;
276
277 /*
278 * Fetch the relevant data from the frame
279 */
280 if (doDfsExtCh) {
281 if (datalen < 3)
282 return AH_FALSE;
283
284 /* Last three bytes of the frame are of interest */
285 pulse_length_pri = *(buf + datalen - 3);
286 pulse_length_ext = *(buf + datalen - 2);
287 pulse_bw_info = *(buf + datalen - 1);
288 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d,"
289 " pulse_length_ext=%d, pulse_bw_info=%x\n",
290 __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext,
291 pulse_bw_info);
292 } else {
293 /* The pulse width is byte 0 of the data */
294 if (datalen >= 1)
295 dur = ((uint8_t) buf[0]) & 0xff;
296 else
297 dur = 0;
298
299 if (dur == 0 && rssi == 0) {
300 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__);
301 return AH_FALSE;
302 }
303
304 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur);
305
306 /* Single-channel only */
307 pri_found = 1;
308 ext_found = 0;
309 }
310
311 /*
312 * If doing extended channel data, pulse_bw_info must
313 * have one of the flags set.
314 */
315 if (doDfsExtCh && pulse_bw_info == 0x0)
316 return AH_FALSE;
317
318 /*
319 * If the extended channel data is available, calculate
320 * which to pay attention to.
321 */
322 if (doDfsExtCh) {
323 /* If pulse is on DC, take the larger duration of the two */
324 if ((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
325 (pulse_bw_info & PRI_CH_RADAR_FOUND)) {
326 is_dc = 1;
327 if (pulse_length_ext > pulse_length_pri) {
328 dur = pulse_length_ext;
329 pri_found = 0;
330 ext_found = 1;
331 } else {
332 dur = pulse_length_pri;
333 pri_found = 1;
334 ext_found = 0;
335 }
336 } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
337 dur = pulse_length_ext;
338 pri_found = 0;
339 ext_found = 1;
340 early_ext = 1;
341 } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
342 dur = pulse_length_pri;
343 pri_found = 1;
344 ext_found = 0;
345 } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
346 dur = pulse_length_ext;
347 pri_found = 0;
348 ext_found = 1;
349 }
350
351 }
352
353 /*
354 * For enhanced DFS (Merlin and later), pulse_bw_info has
355 * implications for selecting the correct RSSI value.
356 */
357 if (doDfsEnhanced) {
358 switch (pulse_bw_info & 0x03) {
359 case 0:
360 /* No radar? */
361 rssi = 0;
362 break;
363 case PRI_CH_RADAR_FOUND:
364 /* Radar in primary channel */
365 /* Cannot use ctrl channel RSSI if ext channel is stronger */
366 if (ext_rssi >= (rssi + 3)) {
367 rssi = 0;
368 }
369 break;
370 case EXT_CH_RADAR_FOUND:
371 /* Radar in extended channel */
372 /* Cannot use ext channel RSSI if ctrl channel is stronger */
373 if (rssi >= (ext_rssi + 12)) {
374 rssi = 0;
375 } else {
376 rssi = ext_rssi;
377 }
378 break;
379 case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
380 /* When both are present, use stronger one */
381 if (rssi < ext_rssi)
382 rssi = ext_rssi;
383 break;
384 }
385 }
386
387 /*
388 * If not doing enhanced DFS, choose the ext channel if
389 * it is stronger than the main channel
390 */
391 if (doDfsExtCh && !doDfsEnhanced) {
392 if ((ext_rssi > rssi) && (ext_rssi < 128))
393 rssi = ext_rssi;
394 }
395
396 /*
397 * XXX what happens if the above code decides the RSSI
398 * XXX wasn't valid, an sets it to 0?
399 */
400
401 /*
402 * Fill out dfs_event structure.
403 */
404 event->re_full_ts = fulltsf;
405 event->re_ts = rxs->rs_tstamp;
406 event->re_rssi = rssi;
407 event->re_dur = dur;
408
409 event->re_flags = 0;
410 if (pri_found)
411 event->re_flags |= HAL_DFS_EVENT_PRICH;
412 if (ext_found)
413 event->re_flags |= HAL_DFS_EVENT_EXTCH;
414 if (early_ext)
415 event->re_flags |= HAL_DFS_EVENT_EXTEARLY;
416 if (is_dc)
417 event->re_flags |= HAL_DFS_EVENT_ISDC;
418
419 return AH_TRUE;
420 }
421
422 /*
423 * Return whether fast-clock is currently enabled for this
424 * channel.
425 */
426 HAL_BOOL
ar5416IsFastClockEnabled(struct ath_hal * ah)427 ar5416IsFastClockEnabled(struct ath_hal *ah)
428 {
429 struct ath_hal_private *ahp = AH_PRIVATE(ah);
430
431 return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan);
432 }
433