xref: /freebsd/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*
2  * Copyright (c) 2010-2011 Atheros Communications, Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  * $FreeBSD$
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
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
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
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
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
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