ath - Reimport fresh from FreeBSD 01-Jan-2014 for re-port
[dragonfly.git] / sys / dev / netif / ath / ath_hal / ar5416 / ar5416_radar.c
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 }