]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ath/if_ath_lna_div.c
tcpdump: remove undesired svn:keywords property from contrib
[FreeBSD/FreeBSD.git] / sys / dev / ath / if_ath_lna_div.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification.
13  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15  *    redistribution must be conditioned upon including a substantially
16  *    similar Disclaimer requirement for further binary redistribution.
17  *
18  * NO WARRANTY
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29  * THE POSSIBILITY OF SUCH DAMAGES.
30  *
31  * $FreeBSD$
32  */
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 /*
37  * This module handles LNA diversity for those chips which implement LNA
38  * mixing (AR9285/AR9485.)
39  */
40 #include "opt_ath.h"
41 #include "opt_inet.h"
42 #include "opt_wlan.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h> 
46 #include <sys/sysctl.h>
47 #include <sys/kernel.h>
48 #include <sys/lock.h>
49 #include <sys/malloc.h>
50 #include <sys/mutex.h>
51 #include <sys/errno.h>
52
53 #include <machine/bus.h>
54 #include <machine/resource.h>
55 #include <sys/bus.h>
56
57 #include <sys/socket.h>
58  
59 #include <net/if.h>
60 #include <net/if_var.h>
61 #include <net/if_media.h>
62 #include <net/if_arp.h>
63 #include <net/ethernet.h>               /* XXX for ether_sprintf */
64
65 #include <net80211/ieee80211_var.h>
66
67 #include <net/bpf.h>
68
69 #ifdef INET
70 #include <netinet/in.h>
71 #include <netinet/if_ether.h>
72 #endif
73
74 #include <dev/ath/if_athvar.h>
75 #include <dev/ath/if_ath_debug.h>
76 #include <dev/ath/if_ath_lna_div.h>
77
78 /* Linux compatibility macros */
79 /*
80  * XXX these don't handle rounding, underflow, overflow, wrapping!
81  */
82 #define msecs_to_jiffies(a)             ( (a) * hz / 1000 )
83
84 /*
85  * Methods which are required
86  */
87
88 /*
89  * Attach the LNA diversity to the given interface
90  */
91 int
92 ath_lna_div_attach(struct ath_softc *sc)
93 {
94         struct if_ath_ant_comb_state *ss;
95         HAL_ANT_COMB_CONFIG div_ant_conf;
96
97         /* Only do this if diversity is enabled */
98         if (! ath_hal_hasdivantcomb(sc->sc_ah))
99                 return (0);
100
101         ss = malloc(sizeof(struct if_ath_ant_comb_state),
102             M_TEMP, M_WAITOK | M_ZERO);
103         if (ss == NULL) {
104                 device_printf(sc->sc_dev, "%s: failed to allocate\n",
105                     __func__);
106                 /* Don't fail at this point */
107                 return (0);
108         }
109
110         /* Fetch the hardware configuration */
111         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
112         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
113
114         /* Figure out what the hardware specific bits should be */
115         if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
116             (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
117                 ss->lna1_lna2_delta = -9;
118         } else {
119                 ss->lna1_lna2_delta = -3;
120         }
121
122         /* Let's flip this on */
123         sc->sc_lna_div = ss;
124         sc->sc_dolnadiv = 1;
125
126         return (0);
127 }
128
129 /*
130  * Detach the LNA diversity state from the given interface
131  */
132 int
133 ath_lna_div_detach(struct ath_softc *sc)
134 {
135         if (sc->sc_lna_div != NULL) {
136                 free(sc->sc_lna_div, M_TEMP);
137                 sc->sc_lna_div = NULL;
138         }
139         sc->sc_dolnadiv = 0;
140         return (0);
141 }
142
143 /*
144  * Enable LNA diversity on the current channel if it's required.
145  */
146 int
147 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
148 {
149
150         return (0);
151 }
152
153 /*
154  * Handle ioctl requests from the diagnostic interface.
155  *
156  * The initial part of this code resembles ath_ioctl_diag();
157  * it's likely a good idea to reduce duplication between
158  * these two routines.
159  */
160 int
161 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
162 {
163         unsigned int id = ad->ad_id & ATH_DIAG_ID;
164         void *indata = NULL;
165         void *outdata = NULL;
166         u_int32_t insize = ad->ad_in_size;
167         u_int32_t outsize = ad->ad_out_size;
168         int error = 0;
169 //      int val;
170
171         if (ad->ad_id & ATH_DIAG_IN) {
172                 /*
173                  * Copy in data.
174                  */
175                 indata = malloc(insize, M_TEMP, M_NOWAIT);
176                 if (indata == NULL) {
177                         error = ENOMEM;
178                         goto bad;
179                 }
180                 error = copyin(ad->ad_in_data, indata, insize);
181                 if (error)
182                         goto bad;
183         }
184         if (ad->ad_id & ATH_DIAG_DYN) {
185                 /*
186                  * Allocate a buffer for the results (otherwise the HAL
187                  * returns a pointer to a buffer where we can read the
188                  * results).  Note that we depend on the HAL leaving this
189                  * pointer for us to use below in reclaiming the buffer;
190                  * may want to be more defensive.
191                  */
192                 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
193                 if (outdata == NULL) {
194                         error = ENOMEM;
195                         goto bad;
196                 }
197         }
198         switch (id) {
199                 default:
200                         error = EINVAL;
201         }
202         if (outsize < ad->ad_out_size)
203                 ad->ad_out_size = outsize;
204         if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
205                 error = EFAULT;
206 bad:
207         if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
208                 free(indata, M_TEMP);
209         if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
210                 free(outdata, M_TEMP);
211         return (error);
212 }
213
214 /*
215  * XXX need to low_rssi_thresh config from ath9k, to support CUS198
216  * antenna diversity correctly.
217  */
218 static HAL_BOOL
219 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
220     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
221 {
222         return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
223                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
224                 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
225 }
226
227 static void
228 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
229     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
230 {
231         antcomb->quick_scan_cnt = 0;
232
233         if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
234                 antcomb->rssi_lna2 = main_rssi_avg;
235         else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
236                 antcomb->rssi_lna1 = main_rssi_avg;
237
238         switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
239         case (0x10): /* LNA2 A-B */
240                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
241                 antcomb->first_quick_scan_conf =
242                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
243                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
244                 break;
245         case (0x20): /* LNA1 A-B */
246                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
247                 antcomb->first_quick_scan_conf =
248                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
249                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
250                 break;
251         case (0x21): /* LNA1 LNA2 */
252                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
253                 antcomb->first_quick_scan_conf =
254                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
255                 antcomb->second_quick_scan_conf =
256                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
257                 break;
258         case (0x12): /* LNA2 LNA1 */
259                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
260                 antcomb->first_quick_scan_conf =
261                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
262                 antcomb->second_quick_scan_conf =
263                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
264                 break;
265         case (0x13): /* LNA2 A+B */
266                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
267                 antcomb->first_quick_scan_conf =
268                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
269                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
270                 break;
271         case (0x23): /* LNA1 A+B */
272                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
273                 antcomb->first_quick_scan_conf =
274                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
275                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
276                 break;
277         default:
278                 break;
279         }
280 }
281
282 static void
283 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
284     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
285     int alt_rssi_avg, int alt_ratio)
286 {
287         /* alt_good */
288         switch (antcomb->quick_scan_cnt) {
289         case 0:
290                 /* set alt to main, and alt to first conf */
291                 div_ant_conf->main_lna_conf = antcomb->main_conf;
292                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
293                 break;
294         case 1:
295                 /* set alt to main, and alt to first conf */
296                 div_ant_conf->main_lna_conf = antcomb->main_conf;
297                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
298                 antcomb->rssi_first = main_rssi_avg;
299                 antcomb->rssi_second = alt_rssi_avg;
300
301                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
302                         /* main is LNA1 */
303                         if (ath_is_alt_ant_ratio_better(alt_ratio,
304                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
305                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
306                                                 main_rssi_avg, alt_rssi_avg,
307                                                 antcomb->total_pkt_count))
308                                 antcomb->first_ratio = AH_TRUE;
309                         else
310                                 antcomb->first_ratio = AH_FALSE;
311                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
312                         if (ath_is_alt_ant_ratio_better(alt_ratio,
313                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
314                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
315                                                 main_rssi_avg, alt_rssi_avg,
316                                                 antcomb->total_pkt_count))
317                                 antcomb->first_ratio = AH_TRUE;
318                         else
319                                 antcomb->first_ratio = AH_FALSE;
320                 } else {
321                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
322                             (alt_rssi_avg > main_rssi_avg +
323                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
324                             (alt_rssi_avg > main_rssi_avg)) &&
325                             (antcomb->total_pkt_count > 50))
326                                 antcomb->first_ratio = AH_TRUE;
327                         else
328                                 antcomb->first_ratio = AH_FALSE;
329                 }
330                 break;
331         case 2:
332                 antcomb->alt_good = AH_FALSE;
333                 antcomb->scan_not_start = AH_FALSE;
334                 antcomb->scan = AH_FALSE;
335                 antcomb->rssi_first = main_rssi_avg;
336                 antcomb->rssi_third = alt_rssi_avg;
337
338                 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
339                         antcomb->rssi_lna1 = alt_rssi_avg;
340                 else if (antcomb->second_quick_scan_conf ==
341                          HAL_ANT_DIV_COMB_LNA2)
342                         antcomb->rssi_lna2 = alt_rssi_avg;
343                 else if (antcomb->second_quick_scan_conf ==
344                          HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
345                         if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
346                                 antcomb->rssi_lna2 = main_rssi_avg;
347                         else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
348                                 antcomb->rssi_lna1 = main_rssi_avg;
349                 }
350
351                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
352                     ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
353                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
354                 else
355                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
356
357                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
358                         if (ath_is_alt_ant_ratio_better(alt_ratio,
359                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
360                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
361                                                 main_rssi_avg, alt_rssi_avg,
362                                                 antcomb->total_pkt_count))
363                                 antcomb->second_ratio = AH_TRUE;
364                         else
365                                 antcomb->second_ratio = AH_FALSE;
366                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
367                         if (ath_is_alt_ant_ratio_better(alt_ratio,
368                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
369                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
370                                                 main_rssi_avg, alt_rssi_avg,
371                                                 antcomb->total_pkt_count))
372                                 antcomb->second_ratio = AH_TRUE;
373                         else
374                                 antcomb->second_ratio = AH_FALSE;
375                 } else {
376                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
377                             (alt_rssi_avg > main_rssi_avg +
378                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
379                             (alt_rssi_avg > main_rssi_avg)) &&
380                             (antcomb->total_pkt_count > 50))
381                                 antcomb->second_ratio = AH_TRUE;
382                         else
383                                 antcomb->second_ratio = AH_FALSE;
384                 }
385
386                 /* set alt to the conf with maximun ratio */
387                 if (antcomb->first_ratio && antcomb->second_ratio) {
388                         if (antcomb->rssi_second > antcomb->rssi_third) {
389                                 /* first alt*/
390                                 if ((antcomb->first_quick_scan_conf ==
391                                     HAL_ANT_DIV_COMB_LNA1) ||
392                                     (antcomb->first_quick_scan_conf ==
393                                     HAL_ANT_DIV_COMB_LNA2))
394                                         /* Set alt LNA1 or LNA2*/
395                                         if (div_ant_conf->main_lna_conf ==
396                                             HAL_ANT_DIV_COMB_LNA2)
397                                                 div_ant_conf->alt_lna_conf =
398                                                         HAL_ANT_DIV_COMB_LNA1;
399                                         else
400                                                 div_ant_conf->alt_lna_conf =
401                                                         HAL_ANT_DIV_COMB_LNA2;
402                                 else
403                                         /* Set alt to A+B or A-B */
404                                         div_ant_conf->alt_lna_conf =
405                                                 antcomb->first_quick_scan_conf;
406                         } else if ((antcomb->second_quick_scan_conf ==
407                                    HAL_ANT_DIV_COMB_LNA1) ||
408                                    (antcomb->second_quick_scan_conf ==
409                                    HAL_ANT_DIV_COMB_LNA2)) {
410                                 /* Set alt LNA1 or LNA2 */
411                                 if (div_ant_conf->main_lna_conf ==
412                                     HAL_ANT_DIV_COMB_LNA2)
413                                         div_ant_conf->alt_lna_conf =
414                                                 HAL_ANT_DIV_COMB_LNA1;
415                                 else
416                                         div_ant_conf->alt_lna_conf =
417                                                 HAL_ANT_DIV_COMB_LNA2;
418                         } else {
419                                 /* Set alt to A+B or A-B */
420                                 div_ant_conf->alt_lna_conf =
421                                         antcomb->second_quick_scan_conf;
422                         }
423                 } else if (antcomb->first_ratio) {
424                         /* first alt */
425                         if ((antcomb->first_quick_scan_conf ==
426                             HAL_ANT_DIV_COMB_LNA1) ||
427                             (antcomb->first_quick_scan_conf ==
428                             HAL_ANT_DIV_COMB_LNA2))
429                                         /* Set alt LNA1 or LNA2 */
430                                 if (div_ant_conf->main_lna_conf ==
431                                     HAL_ANT_DIV_COMB_LNA2)
432                                         div_ant_conf->alt_lna_conf =
433                                                         HAL_ANT_DIV_COMB_LNA1;
434                                 else
435                                         div_ant_conf->alt_lna_conf =
436                                                         HAL_ANT_DIV_COMB_LNA2;
437                         else
438                                 /* Set alt to A+B or A-B */
439                                 div_ant_conf->alt_lna_conf =
440                                                 antcomb->first_quick_scan_conf;
441                 } else if (antcomb->second_ratio) {
442                                 /* second alt */
443                         if ((antcomb->second_quick_scan_conf ==
444                             HAL_ANT_DIV_COMB_LNA1) ||
445                             (antcomb->second_quick_scan_conf ==
446                             HAL_ANT_DIV_COMB_LNA2))
447                                 /* Set alt LNA1 or LNA2 */
448                                 if (div_ant_conf->main_lna_conf ==
449                                     HAL_ANT_DIV_COMB_LNA2)
450                                         div_ant_conf->alt_lna_conf =
451                                                 HAL_ANT_DIV_COMB_LNA1;
452                                 else
453                                         div_ant_conf->alt_lna_conf =
454                                                 HAL_ANT_DIV_COMB_LNA2;
455                         else
456                                 /* Set alt to A+B or A-B */
457                                 div_ant_conf->alt_lna_conf =
458                                                 antcomb->second_quick_scan_conf;
459                 } else {
460                         /* main is largest */
461                         if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
462                             (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
463                                 /* Set alt LNA1 or LNA2 */
464                                 if (div_ant_conf->main_lna_conf ==
465                                     HAL_ANT_DIV_COMB_LNA2)
466                                         div_ant_conf->alt_lna_conf =
467                                                         HAL_ANT_DIV_COMB_LNA1;
468                                 else
469                                         div_ant_conf->alt_lna_conf =
470                                                         HAL_ANT_DIV_COMB_LNA2;
471                         else
472                                 /* Set alt to A+B or A-B */
473                                 div_ant_conf->alt_lna_conf = antcomb->main_conf;
474                 }
475                 break;
476         default:
477                 break;
478         }
479 }
480
481 static void
482 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
483     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
484     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
485 {
486
487         if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
488                 switch ((pdiv_ant_conf->main_lna_conf << 4)
489                     | pdiv_ant_conf->alt_lna_conf) {
490                 case (0x01): //A-B LNA2
491                         pdiv_ant_conf->fast_div_bias = 0x1;
492                         pdiv_ant_conf->main_gaintb   = 0;
493                         pdiv_ant_conf->alt_gaintb    = 0;
494                         break;
495                 case (0x02): //A-B LNA1
496                         pdiv_ant_conf->fast_div_bias = 0x1;
497                         pdiv_ant_conf->main_gaintb   = 0;
498                         pdiv_ant_conf->alt_gaintb    = 0;
499                         break;
500                 case (0x03): //A-B A+B
501                         pdiv_ant_conf->fast_div_bias = 0x1;
502                         pdiv_ant_conf->main_gaintb   = 0;
503                         pdiv_ant_conf->alt_gaintb    = 0;
504                         break;
505                 case (0x10): //LNA2 A-B
506                         if ((antcomb->scan == 0)
507                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
508                                 pdiv_ant_conf->fast_div_bias = 0x3f;
509                         } else {
510                                 pdiv_ant_conf->fast_div_bias = 0x1;
511                         }
512                         pdiv_ant_conf->main_gaintb   = 0;
513                         pdiv_ant_conf->alt_gaintb    = 0;
514                         break;
515                 case (0x12): //LNA2 LNA1
516                         pdiv_ant_conf->fast_div_bias = 0x1;
517                         pdiv_ant_conf->main_gaintb   = 0;
518                         pdiv_ant_conf->alt_gaintb    = 0;
519                         break;
520                         case (0x13): //LNA2 A+B
521                         if ((antcomb->scan == 0)
522                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
523                                 pdiv_ant_conf->fast_div_bias = 0x3f;
524                         } else {
525                                 pdiv_ant_conf->fast_div_bias = 0x1;
526                         }
527                         pdiv_ant_conf->main_gaintb   = 0;
528                         pdiv_ant_conf->alt_gaintb    = 0;
529                         break;
530                 case (0x20): //LNA1 A-B
531                         if ((antcomb->scan == 0)
532                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
533                                 pdiv_ant_conf->fast_div_bias = 0x3f;
534                         } else {
535                                 pdiv_ant_conf->fast_div_bias = 0x1;
536                         }
537                         pdiv_ant_conf->main_gaintb   = 0;
538                         pdiv_ant_conf->alt_gaintb    = 0;
539                         break;
540                 case (0x21): //LNA1 LNA2
541                         pdiv_ant_conf->fast_div_bias = 0x1;
542                         pdiv_ant_conf->main_gaintb   = 0;
543                         pdiv_ant_conf->alt_gaintb    = 0;
544                         break;
545                 case (0x23): //LNA1 A+B
546                         if ((antcomb->scan == 0)
547                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
548                                 pdiv_ant_conf->fast_div_bias = 0x3f;
549                         } else {
550                                 pdiv_ant_conf->fast_div_bias = 0x1;
551                         }
552                         pdiv_ant_conf->main_gaintb   = 0;
553                         pdiv_ant_conf->alt_gaintb    = 0;
554                         break;
555                 case (0x30): //A+B A-B
556                         pdiv_ant_conf->fast_div_bias = 0x1;
557                         pdiv_ant_conf->main_gaintb   = 0;
558                         pdiv_ant_conf->alt_gaintb    = 0;
559                         break;
560                 case (0x31): //A+B LNA2
561                         pdiv_ant_conf->fast_div_bias = 0x1;
562                         pdiv_ant_conf->main_gaintb   = 0;
563                         pdiv_ant_conf->alt_gaintb    = 0;
564                         break;
565                 case (0x32): //A+B LNA1
566                         pdiv_ant_conf->fast_div_bias = 0x1;
567                         pdiv_ant_conf->main_gaintb   = 0;
568                         pdiv_ant_conf->alt_gaintb    = 0;
569                         break;
570                 default:
571                         break;
572                 }
573         } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
574                 switch ((pdiv_ant_conf->main_lna_conf << 4)
575                     | pdiv_ant_conf->alt_lna_conf) {
576                 case (0x01): //A-B LNA2
577                         pdiv_ant_conf->fast_div_bias = 0x1;
578                         pdiv_ant_conf->main_gaintb   = 0;
579                         pdiv_ant_conf->alt_gaintb    = 0;
580                         break;
581                 case (0x02): //A-B LNA1
582                         pdiv_ant_conf->fast_div_bias = 0x1;
583                         pdiv_ant_conf->main_gaintb   = 0;
584                         pdiv_ant_conf->alt_gaintb    = 0;
585                         break;
586                 case (0x03): //A-B A+B
587                         pdiv_ant_conf->fast_div_bias = 0x1;
588                         pdiv_ant_conf->main_gaintb   = 0;
589                         pdiv_ant_conf->alt_gaintb    = 0;
590                         break;
591                 case (0x10): //LNA2 A-B
592                         if ((antcomb->scan == 0)
593                             && (alt_ratio > alt_ant_ratio_th)) {
594                                 pdiv_ant_conf->fast_div_bias = 0x1;
595                         } else {
596                                 pdiv_ant_conf->fast_div_bias = 0x2;
597                         }
598                         pdiv_ant_conf->main_gaintb   = 0;
599                         pdiv_ant_conf->alt_gaintb    = 0;
600                         break;
601                 case (0x12): //LNA2 LNA1
602                         pdiv_ant_conf->fast_div_bias = 0x1;
603                         pdiv_ant_conf->main_gaintb   = 0;
604                         pdiv_ant_conf->alt_gaintb    = 0;
605                         break;
606                 case (0x13): //LNA2 A+B
607                         if ((antcomb->scan == 0)
608                             && (alt_ratio > alt_ant_ratio_th)) {
609                                 pdiv_ant_conf->fast_div_bias = 0x1;
610                         } else {
611                                 pdiv_ant_conf->fast_div_bias = 0x2;
612                         }
613                         pdiv_ant_conf->main_gaintb   = 0;
614                         pdiv_ant_conf->alt_gaintb    = 0;
615                         break;
616                 case (0x20): //LNA1 A-B
617                         if ((antcomb->scan == 0)
618                             && (alt_ratio > alt_ant_ratio_th)) {
619                                 pdiv_ant_conf->fast_div_bias = 0x1;
620                         } else {
621                                 pdiv_ant_conf->fast_div_bias = 0x2;
622                         }
623                         pdiv_ant_conf->main_gaintb   = 0;
624                         pdiv_ant_conf->alt_gaintb    = 0;
625                         break;
626                 case (0x21): //LNA1 LNA2
627                         pdiv_ant_conf->fast_div_bias = 0x1;
628                         pdiv_ant_conf->main_gaintb   = 0;
629                         pdiv_ant_conf->alt_gaintb    = 0;
630                         break;
631                 case (0x23): //LNA1 A+B
632                         if ((antcomb->scan == 0)
633                             && (alt_ratio > alt_ant_ratio_th)) {
634                                 pdiv_ant_conf->fast_div_bias = 0x1;
635                         } else {
636                                 pdiv_ant_conf->fast_div_bias = 0x2;
637                         }
638                         pdiv_ant_conf->main_gaintb   = 0;
639                         pdiv_ant_conf->alt_gaintb    = 0;
640                         break;
641                 case (0x30): //A+B A-B
642                         pdiv_ant_conf->fast_div_bias = 0x1;
643                         pdiv_ant_conf->main_gaintb   = 0;
644                         pdiv_ant_conf->alt_gaintb    = 0;
645                         break;
646                 case (0x31): //A+B LNA2
647                         pdiv_ant_conf->fast_div_bias = 0x1;
648                         pdiv_ant_conf->main_gaintb   = 0;
649                         pdiv_ant_conf->alt_gaintb    = 0;
650                         break;
651                 case (0x32): //A+B LNA1
652                         pdiv_ant_conf->fast_div_bias = 0x1;
653                         pdiv_ant_conf->main_gaintb   = 0;
654                         pdiv_ant_conf->alt_gaintb    = 0;
655                         break;
656                 default:
657                         break;
658                 }
659         } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
660                 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
661                 case (0x01): //A-B LNA2
662                         pdiv_ant_conf->fast_div_bias = 0x3b;
663                         break;
664                 case (0x02): //A-B LNA1
665                         pdiv_ant_conf->fast_div_bias = 0x3d;
666                         break;
667                 case (0x03): //A-B A+B
668                         pdiv_ant_conf->fast_div_bias = 0x1;
669                         break;
670                 case (0x10): //LNA2 A-B
671                         pdiv_ant_conf->fast_div_bias = 0x7;
672                         break;
673                 case (0x12): //LNA2 LNA1
674                         pdiv_ant_conf->fast_div_bias = 0x2;
675                         break;
676                 case (0x13): //LNA2 A+B
677                         pdiv_ant_conf->fast_div_bias = 0x7;
678                         break;
679                 case (0x20): //LNA1 A-B
680                         pdiv_ant_conf->fast_div_bias = 0x6;
681                         break;
682                 case (0x21): //LNA1 LNA2
683                         pdiv_ant_conf->fast_div_bias = 0x0;
684                         break;
685                 case (0x23): //LNA1 A+B
686                         pdiv_ant_conf->fast_div_bias = 0x6;
687                         break;
688                 case (0x30): //A+B A-B
689                         pdiv_ant_conf->fast_div_bias = 0x1;
690                         break;
691                 case (0x31): //A+B LNA2
692                         pdiv_ant_conf->fast_div_bias = 0x3b;
693                         break;
694                 case (0x32): //A+B LNA1
695                         pdiv_ant_conf->fast_div_bias = 0x3d;
696                         break;
697                 default:
698                         break;
699                 }
700         }
701 }
702
703 /*
704  * AR9485/AR933x TODO:
705  * + Select a ratio based on whether RSSI is low or not; but I need
706  *   to figure out what "low_rssi_th" is sourced from.
707  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
708  * + .. and there's likely a bunch of other things to include in this.
709  */
710
711 /* Antenna diversity and combining */
712 void
713 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
714     unsigned long ticks, int hz)
715 {
716         HAL_ANT_COMB_CONFIG div_ant_conf;
717         struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
718         int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
719         int curr_main_set, curr_bias;
720         int main_rssi = rs->rs_rssi_ctl[0];
721         int alt_rssi = rs->rs_rssi_ctl[1];
722         int rx_ant_conf, main_ant_conf, alt_ant_conf;
723         HAL_BOOL short_scan = AH_FALSE;
724
725         rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
726         main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
727         alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
728
729 #if 0
730         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
731             "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
732             "FastDiv: %d\n",
733             __func__,
734             main_rssi,
735             alt_rssi,
736             main_ant_conf,
737             alt_ant_conf,
738             rx_ant_conf,
739             !!(rs->rs_rssi_ctl[2] & 0x80),
740             !!(rs->rs_rssi_ctl[2] & 0x40),
741             !!(rs->rs_rssi_ext[2] & 0x40));
742 #endif
743
744         /*
745          * If LNA diversity combining isn't enabled, don't run this.
746          */
747         if (! sc->sc_dolnadiv)
748                 return;
749
750         /*
751          * XXX this is ugly, but the HAL code attaches the
752          * LNA diversity to the TX antenna settings.
753          * I don't know why.
754          */
755         if (sc->sc_txantenna != HAL_ANT_VARIABLE)
756                 return;
757
758         /* Record packet only when alt_rssi is positive */
759         if (main_rssi > 0 && alt_rssi > 0) {
760                 antcomb->total_pkt_count++;
761                 antcomb->main_total_rssi += main_rssi;
762                 antcomb->alt_total_rssi  += alt_rssi;
763                 if (main_ant_conf == rx_ant_conf)
764                         antcomb->main_recv_cnt++;
765                 else
766                         antcomb->alt_recv_cnt++;
767         }
768
769         /* Short scan check */
770         if (antcomb->scan && antcomb->alt_good) {
771                 if (ieee80211_time_after(ticks, antcomb->scan_start_time +
772                     msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
773                         short_scan = AH_TRUE;
774                 else
775                         if (antcomb->total_pkt_count ==
776                             ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
777                                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
778                                             antcomb->total_pkt_count);
779                                 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
780                                         short_scan = AH_TRUE;
781                         }
782         }
783
784 #if 0
785         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
786             "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
787             __func__,
788             antcomb->total_pkt_count,
789             !! (rs->rs_moreaggr),
790             !! (short_scan));
791 #endif
792
793         if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
794             rs->rs_moreaggr) && !short_scan)
795                 return;
796
797         if (antcomb->total_pkt_count) {
798                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
799                              antcomb->total_pkt_count);
800                 main_rssi_avg = (antcomb->main_total_rssi /
801                                  antcomb->total_pkt_count);
802                 alt_rssi_avg = (antcomb->alt_total_rssi /
803                                  antcomb->total_pkt_count);
804         }
805
806         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
807
808         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
809         curr_alt_set = div_ant_conf.alt_lna_conf;
810         curr_main_set = div_ant_conf.main_lna_conf;
811         curr_bias = div_ant_conf.fast_div_bias;
812
813         antcomb->count++;
814
815         if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
816                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
817                         ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
818                                                   main_rssi_avg);
819                         antcomb->alt_good = AH_TRUE;
820                 } else {
821                         antcomb->alt_good = AH_FALSE;
822                 }
823
824                 antcomb->count = 0;
825                 antcomb->scan = AH_TRUE;
826                 antcomb->scan_not_start = AH_TRUE;
827         }
828
829         if (!antcomb->scan) {
830                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
831                         if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
832                                 /* Switch main and alt LNA */
833                                 div_ant_conf.main_lna_conf =
834                                                 HAL_ANT_DIV_COMB_LNA2;
835                                 div_ant_conf.alt_lna_conf  =
836                                                 HAL_ANT_DIV_COMB_LNA1;
837                         } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
838                                 div_ant_conf.main_lna_conf =
839                                                 HAL_ANT_DIV_COMB_LNA1;
840                                 div_ant_conf.alt_lna_conf  =
841                                                 HAL_ANT_DIV_COMB_LNA2;
842                         }
843
844                         goto div_comb_done;
845                 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
846                            (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
847                         /* Set alt to another LNA */
848                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
849                                 div_ant_conf.alt_lna_conf =
850                                                 HAL_ANT_DIV_COMB_LNA1;
851                         else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
852                                 div_ant_conf.alt_lna_conf =
853                                                 HAL_ANT_DIV_COMB_LNA2;
854
855                         goto div_comb_done;
856                 }
857
858                 if ((alt_rssi_avg < (main_rssi_avg +
859                     antcomb->lna1_lna2_delta)))
860                         goto div_comb_done;
861         }
862
863         if (!antcomb->scan_not_start) {
864                 switch (curr_alt_set) {
865                 case HAL_ANT_DIV_COMB_LNA2:
866                         antcomb->rssi_lna2 = alt_rssi_avg;
867                         antcomb->rssi_lna1 = main_rssi_avg;
868                         antcomb->scan = AH_TRUE;
869                         /* set to A+B */
870                         div_ant_conf.main_lna_conf =
871                                 HAL_ANT_DIV_COMB_LNA1;
872                         div_ant_conf.alt_lna_conf  =
873                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
874                         break;
875                 case HAL_ANT_DIV_COMB_LNA1:
876                         antcomb->rssi_lna1 = alt_rssi_avg;
877                         antcomb->rssi_lna2 = main_rssi_avg;
878                         antcomb->scan = AH_TRUE;
879                         /* set to A+B */
880                         div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
881                         div_ant_conf.alt_lna_conf  =
882                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
883                         break;
884                 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
885                         antcomb->rssi_add = alt_rssi_avg;
886                         antcomb->scan = AH_TRUE;
887                         /* set to A-B */
888                         div_ant_conf.alt_lna_conf =
889                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
890                         break;
891                 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
892                         antcomb->rssi_sub = alt_rssi_avg;
893                         antcomb->scan = AH_FALSE;
894                         if (antcomb->rssi_lna2 >
895                             (antcomb->rssi_lna1 +
896                             ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
897                                 /* use LNA2 as main LNA */
898                                 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
899                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
900                                         /* set to A+B */
901                                         div_ant_conf.main_lna_conf =
902                                                 HAL_ANT_DIV_COMB_LNA2;
903                                         div_ant_conf.alt_lna_conf  =
904                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
905                                 } else if (antcomb->rssi_sub >
906                                            antcomb->rssi_lna1) {
907                                         /* set to A-B */
908                                         div_ant_conf.main_lna_conf =
909                                                 HAL_ANT_DIV_COMB_LNA2;
910                                         div_ant_conf.alt_lna_conf =
911                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
912                                 } else {
913                                         /* set to LNA1 */
914                                         div_ant_conf.main_lna_conf =
915                                                 HAL_ANT_DIV_COMB_LNA2;
916                                         div_ant_conf.alt_lna_conf =
917                                                 HAL_ANT_DIV_COMB_LNA1;
918                                 }
919                         } else {
920                                 /* use LNA1 as main LNA */
921                                 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
922                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
923                                         /* set to A+B */
924                                         div_ant_conf.main_lna_conf =
925                                                 HAL_ANT_DIV_COMB_LNA1;
926                                         div_ant_conf.alt_lna_conf  =
927                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
928                                 } else if (antcomb->rssi_sub >
929                                            antcomb->rssi_lna1) {
930                                         /* set to A-B */
931                                         div_ant_conf.main_lna_conf =
932                                                 HAL_ANT_DIV_COMB_LNA1;
933                                         div_ant_conf.alt_lna_conf =
934                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
935                                 } else {
936                                         /* set to LNA2 */
937                                         div_ant_conf.main_lna_conf =
938                                                 HAL_ANT_DIV_COMB_LNA1;
939                                         div_ant_conf.alt_lna_conf =
940                                                 HAL_ANT_DIV_COMB_LNA2;
941                                 }
942                         }
943                         break;
944                 default:
945                         break;
946                 }
947         } else {
948                 if (!antcomb->alt_good) {
949                         antcomb->scan_not_start = AH_FALSE;
950                         /* Set alt to another LNA */
951                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
952                                 div_ant_conf.main_lna_conf =
953                                                 HAL_ANT_DIV_COMB_LNA2;
954                                 div_ant_conf.alt_lna_conf =
955                                                 HAL_ANT_DIV_COMB_LNA1;
956                         } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
957                                 div_ant_conf.main_lna_conf =
958                                                 HAL_ANT_DIV_COMB_LNA1;
959                                 div_ant_conf.alt_lna_conf =
960                                                 HAL_ANT_DIV_COMB_LNA2;
961                         }
962                         goto div_comb_done;
963                 }
964         }
965
966         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
967                                            main_rssi_avg, alt_rssi_avg,
968                                            alt_ratio);
969
970         antcomb->quick_scan_cnt++;
971
972 div_comb_done:
973 #if 0
974         ath_ant_div_conf_fast_divbias(&div_ant_conf);
975 #endif
976
977         ath_ant_adjust_fast_divbias(antcomb,
978             alt_ratio,
979             ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
980             div_ant_conf.antdiv_configgroup,
981             &div_ant_conf);
982
983         ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
984
985         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
986            __func__, antcomb->total_pkt_count);
987
988         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
989            __func__, antcomb->main_total_rssi);
990         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
991            __func__, antcomb->alt_total_rssi);
992
993         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
994            __func__, main_rssi_avg);
995         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
996            __func__, alt_rssi_avg);
997
998         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
999            __func__, antcomb->main_recv_cnt);
1000         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
1001            __func__, antcomb->alt_recv_cnt);
1002
1003 //      if (curr_alt_set != div_ant_conf.alt_lna_conf)
1004                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1005                     __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1006 //      if (curr_main_set != div_ant_conf.main_lna_conf)
1007                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1008                     __func__, curr_main_set, div_ant_conf.main_lna_conf);
1009 //      if (curr_bias != div_ant_conf.fast_div_bias)
1010                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1011                     __func__, curr_bias, div_ant_conf.fast_div_bias);
1012
1013         antcomb->scan_start_time = ticks;
1014         antcomb->total_pkt_count = 0;
1015         antcomb->main_total_rssi = 0;
1016         antcomb->alt_total_rssi = 0;
1017         antcomb->main_recv_cnt = 0;
1018         antcomb->alt_recv_cnt = 0;
1019 }
1020