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
53HAL_BOOL
54ar5416GetDfsDefaultThresh(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 */
80void
81ar5416GetDfsThresh(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 */
122void
123ar5416EnableDfs(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
232HAL_BOOL
233ar5416ProcessRadarEvent(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 */
426HAL_BOOL
427ar5416IsFastClockEnabled(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