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