ar5416_radar.c revision 231927
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: head/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c 231927 2012-02-20 03:07:07Z adrian $
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 */
35void
36ar5416GetDfsThresh(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 */
77void
78ar5416EnableDfs(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
187HAL_BOOL
188ar5416ProcessRadarEvent(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 */
381HAL_BOOL
382ar5416IsFastClockEnabled(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