xref: /freebsd/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c (revision 3ef51c5fb9163f2aafb1c14729e06a8bf0c4d113)
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  * Get the radar parameter values and return them in the pe
33  * structure
34  */
35 void
36 ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
37 {
38 	uint32_t val, temp;
39 
40 	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
41 
42 	temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
43 	temp |= 0xFFFFFF80;
44 	pe->pe_firpwr = temp;
45 	pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
46 	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
47 	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
48 	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
49 
50 	/* RADAR_1 values */
51 	val = OS_REG_READ(ah, AR_PHY_RADAR_1);
52 	pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH);
53 	pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH);
54 	pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN);
55 
56 	pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) &
57 	    AR_PHY_RADAR_EXT_ENA);
58 
59 	pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
60 	    AR_PHY_RADAR_1_USE_FIR128);
61 	pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
62 	    AR_PHY_RADAR_1_BLOCK_CHECK);
63 	pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
64 	    AR_PHY_RADAR_1_MAX_RRSSI);
65 	pe->pe_enabled = !!
66 	    (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA);
67 	pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
68 	    AR_PHY_RADAR_1_RELPWR_ENA);
69 	pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
70 	    AR_PHY_RADAR_1_RELSTEP_CHECK);
71 }
72 
73 /*
74  * Enable radar detection and set the radar parameters per the
75  * values in pe
76  */
77 void
78 ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
79 {
80 	uint32_t val;
81 
82 	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
83 
84 	if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
85 		val &= ~AR_PHY_RADAR_0_FIRPWR;
86 		val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
87 	}
88 	if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
89 		val &= ~AR_PHY_RADAR_0_RRSSI;
90 		val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
91 	}
92 	if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
93 		val &= ~AR_PHY_RADAR_0_HEIGHT;
94 		val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
95 	}
96 	if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
97 		val &= ~AR_PHY_RADAR_0_PRSSI;
98 		val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
99 	}
100 	if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
101 		val &= ~AR_PHY_RADAR_0_INBAND;
102 		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
103 	}
104 
105 	/*Enable FFT data*/
106 	val |= AR_PHY_RADAR_0_FFT_ENA;
107 	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val);
108 
109 	/* Implicitly enable */
110 	if (pe->pe_enabled == 1)
111 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
112 	else if (pe->pe_enabled == 0)
113 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
114 
115 	if (pe->pe_usefir128 == 1)
116 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
117 	else if (pe->pe_usefir128 == 0)
118 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
119 
120 	if (pe->pe_enmaxrssi == 1)
121 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
122 	else if (pe->pe_enmaxrssi == 0)
123 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
124 
125 	if (pe->pe_blockradar == 1)
126 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
127 	else if (pe->pe_blockradar == 0)
128 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
129 
130 	if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) {
131 		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
132 		val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH;
133 		val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH);
134 		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
135 	}
136 	if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) {
137 		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
138 		val &= ~AR_PHY_RADAR_1_RELPWR_THRESH;
139 		val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH);
140 		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
141 	}
142 
143 	if (pe->pe_en_relstep_check == 1)
144 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
145 		    AR_PHY_RADAR_1_RELSTEP_CHECK);
146 	else if (pe->pe_en_relstep_check == 0)
147 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
148 		    AR_PHY_RADAR_1_RELSTEP_CHECK);
149 
150 	if (pe->pe_enrelpwr == 1)
151 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
152 		    AR_PHY_RADAR_1_RELPWR_ENA);
153 	else if (pe->pe_enrelpwr == 0)
154 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
155 		    AR_PHY_RADAR_1_RELPWR_ENA);
156 
157 	if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) {
158 		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
159 		val &= ~AR_PHY_RADAR_1_MAXLEN;
160 		val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN);
161 		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
162 	}
163 
164 	/*
165 	 * Enable HT/40 if the upper layer asks;
166 	 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS
167 	 * is available.
168 	 */
169 	if (pe->pe_extchannel == 1)
170 		OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
171 	else if (pe->pe_extchannel == 0)
172 		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
173 }
174 
175 /*
176  * Extract the radar event information from the given phy error.
177  *
178  * Returns AH_TRUE if the phy error was actually a phy error,
179  * AH_FALSE if the phy error wasn't a phy error.
180  */
181 
182 /* Flags for pulse_bw_info */
183 #define	PRI_CH_RADAR_FOUND		0x01
184 #define	EXT_CH_RADAR_FOUND		0x02
185 #define	EXT_CH_RADAR_EARLY_FOUND	0x04
186 
187 HAL_BOOL
188 ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
189     uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
190 {
191 	HAL_BOOL doDfsExtCh;
192 	HAL_BOOL doDfsEnhanced;
193 	HAL_BOOL doDfsCombinedRssi;
194 
195 	uint8_t rssi = 0, ext_rssi = 0;
196 	uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0;
197 	uint32_t dur = 0;
198 	int pri_found = 1, ext_found = 0;
199 	int early_ext = 0;
200 	int is_dc = 0;
201 	uint16_t datalen;		/* length from the RX status field */
202 
203 	/* Check whether the given phy error is a radar event */
204 	if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) &&
205 	    (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) {
206 		return AH_FALSE;
207 	}
208 
209 	/* Grab copies of the capabilities; just to make the code clearer */
210 	doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport;
211 	doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport;
212 	doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi;
213 
214 	datalen = rxs->rs_datalen;
215 
216 	/* If hardware supports it, use combined RSSI, else use chain 0 RSSI */
217 	if (doDfsCombinedRssi)
218 		rssi = (uint8_t) rxs->rs_rssi;
219 	else
220 		rssi = (uint8_t) rxs->rs_rssi_ctl[0];
221 
222 	/* Set this; but only use it if doDfsExtCh is set */
223 	ext_rssi = (uint8_t) rxs->rs_rssi_ext[0];
224 
225 	/* Cap it at 0 if the RSSI is a negative number */
226 	if (rssi & 0x80)
227 		rssi = 0;
228 
229 	if (ext_rssi & 0x80)
230 		ext_rssi = 0;
231 
232 	/*
233 	 * Fetch the relevant data from the frame
234 	 */
235 	if (doDfsExtCh) {
236 		if (datalen < 3)
237 			return AH_FALSE;
238 
239 		/* Last three bytes of the frame are of interest */
240 		pulse_length_pri = *(buf + datalen - 3);
241 		pulse_length_ext = *(buf + datalen - 2);
242 		pulse_bw_info = *(buf + datalen - 1);
243 		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d,"
244 		    " pulse_length_ext=%d, pulse_bw_info=%x\n",
245 		    __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext,
246 		    pulse_bw_info);
247 	} else {
248 		/* The pulse width is byte 0 of the data */
249 		if (datalen >= 1)
250 			dur = ((uint8_t) buf[0]) & 0xff;
251 		else
252 			dur = 0;
253 
254 		if (dur == 0 && rssi == 0) {
255 			HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__);
256 			return AH_FALSE;
257 		}
258 
259 		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur);
260 
261 		/* Single-channel only */
262 		pri_found = 1;
263 		ext_found = 0;
264 	}
265 
266 	/*
267 	 * If doing extended channel data, pulse_bw_info must
268 	 * have one of the flags set.
269 	 */
270 	if (doDfsExtCh && pulse_bw_info == 0x0)
271 		return AH_FALSE;
272 
273 	/*
274 	 * If the extended channel data is available, calculate
275 	 * which to pay attention to.
276 	 */
277 	if (doDfsExtCh) {
278 		/* If pulse is on DC, take the larger duration of the two */
279 		if ((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
280 		    (pulse_bw_info & PRI_CH_RADAR_FOUND)) {
281 			is_dc = 1;
282 			if (pulse_length_ext > pulse_length_pri) {
283 				dur = pulse_length_ext;
284 				pri_found = 0;
285 				ext_found = 1;
286 			} else {
287 				dur = pulse_length_pri;
288 				pri_found = 1;
289 				ext_found = 0;
290 			}
291 		} else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
292 			dur = pulse_length_ext;
293 			pri_found = 0;
294 			ext_found = 1;
295 			early_ext = 1;
296 		} else if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
297 			dur = pulse_length_pri;
298 			pri_found = 1;
299 			ext_found = 0;
300 		} else if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
301 			dur = pulse_length_ext;
302 			pri_found = 0;
303 			ext_found = 1;
304 		}
305 
306 	}
307 
308 	/*
309 	 * For enhanced DFS (Merlin and later), pulse_bw_info has
310 	 * implications for selecting the correct RSSI value.
311 	 */
312 	if (doDfsEnhanced) {
313 		switch (pulse_bw_info & 0x03) {
314 		case 0:
315 			/* No radar? */
316 			rssi = 0;
317 			break;
318 		case PRI_CH_RADAR_FOUND:
319 			/* Radar in primary channel */
320 			/* Cannot use ctrl channel RSSI if ext channel is stronger */
321 			if (ext_rssi >= (rssi + 3)) {
322 				rssi = 0;
323 			};
324 			break;
325 		case EXT_CH_RADAR_FOUND:
326 			/* Radar in extended channel */
327 			/* Cannot use ext channel RSSI if ctrl channel is stronger */
328 			if (rssi >= (ext_rssi + 12)) {
329 				rssi = 0;
330 			} else {
331 				rssi = ext_rssi;
332 			}
333 			break;
334 		case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
335 			/* When both are present, use stronger one */
336 			if (rssi < ext_rssi)
337 				rssi = ext_rssi;
338 			break;
339 		}
340 	}
341 
342 	/*
343 	 * If not doing enhanced DFS, choose the ext channel if
344 	 * it is stronger than the main channel
345 	 */
346 	if (doDfsExtCh && !doDfsEnhanced) {
347 		if ((ext_rssi > rssi) && (ext_rssi < 128))
348 			rssi = ext_rssi;
349 	}
350 
351 	/*
352 	 * XXX what happens if the above code decides the RSSI
353 	 * XXX wasn't valid, an sets it to 0?
354 	 */
355 
356 	/*
357 	 * Fill out dfs_event structure.
358 	 */
359 	event->re_full_ts = fulltsf;
360 	event->re_ts = rxs->rs_tstamp;
361 	event->re_rssi = rssi;
362 	event->re_dur = dur;
363 
364 	event->re_flags = 0;
365 	if (pri_found)
366 		event->re_flags |= HAL_DFS_EVENT_PRICH;
367 	if (ext_found)
368 		event->re_flags |= HAL_DFS_EVENT_EXTCH;
369 	if (early_ext)
370 		event->re_flags |= HAL_DFS_EVENT_EXTEARLY;
371 	if (is_dc)
372 		event->re_flags |= HAL_DFS_EVENT_ISDC;
373 
374 	return AH_TRUE;
375 }
376 
377 /*
378  * Return whether fast-clock is currently enabled for this
379  * channel.
380  */
381 HAL_BOOL
382 ar5416IsFastClockEnabled(struct ath_hal *ah)
383 {
384 	struct ath_hal_private *ahp = AH_PRIVATE(ah);
385 
386 	return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan);
387 }
388