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