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