wlan - Sync dev/netif/ath from FreeBSD part 4/N
[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 __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 compatibility 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 /*
213  * XXX need to low_rssi_thresh config from ath9k, to support CUS198
214  * antenna diversity correctly.
215  */
216 static HAL_BOOL
217 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
218     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
219 {
220         return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
221                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
222                 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
223 }
224
225 static void
226 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
227     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
228 {
229         antcomb->quick_scan_cnt = 0;
230
231         if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
232                 antcomb->rssi_lna2 = main_rssi_avg;
233         else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
234                 antcomb->rssi_lna1 = main_rssi_avg;
235
236         switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
237         case (0x10): /* LNA2 A-B */
238                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
239                 antcomb->first_quick_scan_conf =
240                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
241                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
242                 break;
243         case (0x20): /* LNA1 A-B */
244                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
245                 antcomb->first_quick_scan_conf =
246                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
247                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
248                 break;
249         case (0x21): /* LNA1 LNA2 */
250                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
251                 antcomb->first_quick_scan_conf =
252                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
253                 antcomb->second_quick_scan_conf =
254                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
255                 break;
256         case (0x12): /* LNA2 LNA1 */
257                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
258                 antcomb->first_quick_scan_conf =
259                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
260                 antcomb->second_quick_scan_conf =
261                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
262                 break;
263         case (0x13): /* LNA2 A+B */
264                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
265                 antcomb->first_quick_scan_conf =
266                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
267                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
268                 break;
269         case (0x23): /* LNA1 A+B */
270                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
271                 antcomb->first_quick_scan_conf =
272                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
273                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
274                 break;
275         default:
276                 break;
277         }
278 }
279
280 static void
281 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
282     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
283     int alt_rssi_avg, int alt_ratio)
284 {
285         /* alt_good */
286         switch (antcomb->quick_scan_cnt) {
287         case 0:
288                 /* set alt to main, and alt to first conf */
289                 div_ant_conf->main_lna_conf = antcomb->main_conf;
290                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
291                 break;
292         case 1:
293                 /* set alt to main, and alt to first conf */
294                 div_ant_conf->main_lna_conf = antcomb->main_conf;
295                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
296                 antcomb->rssi_first = main_rssi_avg;
297                 antcomb->rssi_second = alt_rssi_avg;
298
299                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
300                         /* main is LNA1 */
301                         if (ath_is_alt_ant_ratio_better(alt_ratio,
302                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
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 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
310                         if (ath_is_alt_ant_ratio_better(alt_ratio,
311                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
312                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
313                                                 main_rssi_avg, alt_rssi_avg,
314                                                 antcomb->total_pkt_count))
315                                 antcomb->first_ratio = AH_TRUE;
316                         else
317                                 antcomb->first_ratio = AH_FALSE;
318                 } else {
319                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
320                             (alt_rssi_avg > main_rssi_avg +
321                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
322                             (alt_rssi_avg > main_rssi_avg)) &&
323                             (antcomb->total_pkt_count > 50))
324                                 antcomb->first_ratio = AH_TRUE;
325                         else
326                                 antcomb->first_ratio = AH_FALSE;
327                 }
328                 break;
329         case 2:
330                 antcomb->alt_good = AH_FALSE;
331                 antcomb->scan_not_start = AH_FALSE;
332                 antcomb->scan = AH_FALSE;
333                 antcomb->rssi_first = main_rssi_avg;
334                 antcomb->rssi_third = alt_rssi_avg;
335
336                 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
337                         antcomb->rssi_lna1 = alt_rssi_avg;
338                 else if (antcomb->second_quick_scan_conf ==
339                          HAL_ANT_DIV_COMB_LNA2)
340                         antcomb->rssi_lna2 = alt_rssi_avg;
341                 else if (antcomb->second_quick_scan_conf ==
342                          HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
343                         if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
344                                 antcomb->rssi_lna2 = main_rssi_avg;
345                         else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
346                                 antcomb->rssi_lna1 = main_rssi_avg;
347                 }
348
349                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
350                     ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
351                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
352                 else
353                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
354
355                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
356                         if (ath_is_alt_ant_ratio_better(alt_ratio,
357                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
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 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
365                         if (ath_is_alt_ant_ratio_better(alt_ratio,
366                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
367                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
368                                                 main_rssi_avg, alt_rssi_avg,
369                                                 antcomb->total_pkt_count))
370                                 antcomb->second_ratio = AH_TRUE;
371                         else
372                                 antcomb->second_ratio = AH_FALSE;
373                 } else {
374                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
375                             (alt_rssi_avg > main_rssi_avg +
376                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
377                             (alt_rssi_avg > main_rssi_avg)) &&
378                             (antcomb->total_pkt_count > 50))
379                                 antcomb->second_ratio = AH_TRUE;
380                         else
381                                 antcomb->second_ratio = AH_FALSE;
382                 }
383
384                 /* set alt to the conf with maximun ratio */
385                 if (antcomb->first_ratio && antcomb->second_ratio) {
386                         if (antcomb->rssi_second > antcomb->rssi_third) {
387                                 /* first alt*/
388                                 if ((antcomb->first_quick_scan_conf ==
389                                     HAL_ANT_DIV_COMB_LNA1) ||
390                                     (antcomb->first_quick_scan_conf ==
391                                     HAL_ANT_DIV_COMB_LNA2))
392                                         /* Set alt LNA1 or LNA2*/
393                                         if (div_ant_conf->main_lna_conf ==
394                                             HAL_ANT_DIV_COMB_LNA2)
395                                                 div_ant_conf->alt_lna_conf =
396                                                         HAL_ANT_DIV_COMB_LNA1;
397                                         else
398                                                 div_ant_conf->alt_lna_conf =
399                                                         HAL_ANT_DIV_COMB_LNA2;
400                                 else
401                                         /* Set alt to A+B or A-B */
402                                         div_ant_conf->alt_lna_conf =
403                                                 antcomb->first_quick_scan_conf;
404                         } else if ((antcomb->second_quick_scan_conf ==
405                                    HAL_ANT_DIV_COMB_LNA1) ||
406                                    (antcomb->second_quick_scan_conf ==
407                                    HAL_ANT_DIV_COMB_LNA2)) {
408                                 /* Set alt LNA1 or LNA2 */
409                                 if (div_ant_conf->main_lna_conf ==
410                                     HAL_ANT_DIV_COMB_LNA2)
411                                         div_ant_conf->alt_lna_conf =
412                                                 HAL_ANT_DIV_COMB_LNA1;
413                                 else
414                                         div_ant_conf->alt_lna_conf =
415                                                 HAL_ANT_DIV_COMB_LNA2;
416                         } else {
417                                 /* Set alt to A+B or A-B */
418                                 div_ant_conf->alt_lna_conf =
419                                         antcomb->second_quick_scan_conf;
420                         }
421                 } else if (antcomb->first_ratio) {
422                         /* first alt */
423                         if ((antcomb->first_quick_scan_conf ==
424                             HAL_ANT_DIV_COMB_LNA1) ||
425                             (antcomb->first_quick_scan_conf ==
426                             HAL_ANT_DIV_COMB_LNA2))
427                                         /* Set alt LNA1 or LNA2 */
428                                 if (div_ant_conf->main_lna_conf ==
429                                     HAL_ANT_DIV_COMB_LNA2)
430                                         div_ant_conf->alt_lna_conf =
431                                                         HAL_ANT_DIV_COMB_LNA1;
432                                 else
433                                         div_ant_conf->alt_lna_conf =
434                                                         HAL_ANT_DIV_COMB_LNA2;
435                         else
436                                 /* Set alt to A+B or A-B */
437                                 div_ant_conf->alt_lna_conf =
438                                                 antcomb->first_quick_scan_conf;
439                 } else if (antcomb->second_ratio) {
440                                 /* second alt */
441                         if ((antcomb->second_quick_scan_conf ==
442                             HAL_ANT_DIV_COMB_LNA1) ||
443                             (antcomb->second_quick_scan_conf ==
444                             HAL_ANT_DIV_COMB_LNA2))
445                                 /* Set alt LNA1 or LNA2 */
446                                 if (div_ant_conf->main_lna_conf ==
447                                     HAL_ANT_DIV_COMB_LNA2)
448                                         div_ant_conf->alt_lna_conf =
449                                                 HAL_ANT_DIV_COMB_LNA1;
450                                 else
451                                         div_ant_conf->alt_lna_conf =
452                                                 HAL_ANT_DIV_COMB_LNA2;
453                         else
454                                 /* Set alt to A+B or A-B */
455                                 div_ant_conf->alt_lna_conf =
456                                                 antcomb->second_quick_scan_conf;
457                 } else {
458                         /* main is largest */
459                         if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
460                             (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
461                                 /* Set alt LNA1 or LNA2 */
462                                 if (div_ant_conf->main_lna_conf ==
463                                     HAL_ANT_DIV_COMB_LNA2)
464                                         div_ant_conf->alt_lna_conf =
465                                                         HAL_ANT_DIV_COMB_LNA1;
466                                 else
467                                         div_ant_conf->alt_lna_conf =
468                                                         HAL_ANT_DIV_COMB_LNA2;
469                         else
470                                 /* Set alt to A+B or A-B */
471                                 div_ant_conf->alt_lna_conf = antcomb->main_conf;
472                 }
473                 break;
474         default:
475                 break;
476         }
477 }
478
479 static void
480 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
481     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
482     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
483 {
484
485         if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
486                 switch ((pdiv_ant_conf->main_lna_conf << 4)
487                     | pdiv_ant_conf->alt_lna_conf) {
488                 case (0x01): //A-B LNA2
489                         pdiv_ant_conf->fast_div_bias = 0x1;
490                         pdiv_ant_conf->main_gaintb   = 0;
491                         pdiv_ant_conf->alt_gaintb    = 0;
492                         break;
493                 case (0x02): //A-B LNA1
494                         pdiv_ant_conf->fast_div_bias = 0x1;
495                         pdiv_ant_conf->main_gaintb   = 0;
496                         pdiv_ant_conf->alt_gaintb    = 0;
497                         break;
498                 case (0x03): //A-B A+B
499                         pdiv_ant_conf->fast_div_bias = 0x1;
500                         pdiv_ant_conf->main_gaintb   = 0;
501                         pdiv_ant_conf->alt_gaintb    = 0;
502                         break;
503                 case (0x10): //LNA2 A-B
504                         if ((antcomb->scan == 0)
505                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
506                                 pdiv_ant_conf->fast_div_bias = 0x3f;
507                         } else {
508                                 pdiv_ant_conf->fast_div_bias = 0x1;
509                         }
510                         pdiv_ant_conf->main_gaintb   = 0;
511                         pdiv_ant_conf->alt_gaintb    = 0;
512                         break;
513                 case (0x12): //LNA2 LNA1
514                         pdiv_ant_conf->fast_div_bias = 0x1;
515                         pdiv_ant_conf->main_gaintb   = 0;
516                         pdiv_ant_conf->alt_gaintb    = 0;
517                         break;
518                         case (0x13): //LNA2 A+B
519                         if ((antcomb->scan == 0)
520                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
521                                 pdiv_ant_conf->fast_div_bias = 0x3f;
522                         } else {
523                                 pdiv_ant_conf->fast_div_bias = 0x1;
524                         }
525                         pdiv_ant_conf->main_gaintb   = 0;
526                         pdiv_ant_conf->alt_gaintb    = 0;
527                         break;
528                 case (0x20): //LNA1 A-B
529                         if ((antcomb->scan == 0)
530                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
531                                 pdiv_ant_conf->fast_div_bias = 0x3f;
532                         } else {
533                                 pdiv_ant_conf->fast_div_bias = 0x1;
534                         }
535                         pdiv_ant_conf->main_gaintb   = 0;
536                         pdiv_ant_conf->alt_gaintb    = 0;
537                         break;
538                 case (0x21): //LNA1 LNA2
539                         pdiv_ant_conf->fast_div_bias = 0x1;
540                         pdiv_ant_conf->main_gaintb   = 0;
541                         pdiv_ant_conf->alt_gaintb    = 0;
542                         break;
543                 case (0x23): //LNA1 A+B
544                         if ((antcomb->scan == 0)
545                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
546                                 pdiv_ant_conf->fast_div_bias = 0x3f;
547                         } else {
548                                 pdiv_ant_conf->fast_div_bias = 0x1;
549                         }
550                         pdiv_ant_conf->main_gaintb   = 0;
551                         pdiv_ant_conf->alt_gaintb    = 0;
552                         break;
553                 case (0x30): //A+B A-B
554                         pdiv_ant_conf->fast_div_bias = 0x1;
555                         pdiv_ant_conf->main_gaintb   = 0;
556                         pdiv_ant_conf->alt_gaintb    = 0;
557                         break;
558                 case (0x31): //A+B LNA2
559                         pdiv_ant_conf->fast_div_bias = 0x1;
560                         pdiv_ant_conf->main_gaintb   = 0;
561                         pdiv_ant_conf->alt_gaintb    = 0;
562                         break;
563                 case (0x32): //A+B LNA1
564                         pdiv_ant_conf->fast_div_bias = 0x1;
565                         pdiv_ant_conf->main_gaintb   = 0;
566                         pdiv_ant_conf->alt_gaintb    = 0;
567                         break;
568                 default:
569                         break;
570                 }
571         } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
572                 switch ((pdiv_ant_conf->main_lna_conf << 4)
573                     | pdiv_ant_conf->alt_lna_conf) {
574                 case (0x01): //A-B LNA2
575                         pdiv_ant_conf->fast_div_bias = 0x1;
576                         pdiv_ant_conf->main_gaintb   = 0;
577                         pdiv_ant_conf->alt_gaintb    = 0;
578                         break;
579                 case (0x02): //A-B LNA1
580                         pdiv_ant_conf->fast_div_bias = 0x1;
581                         pdiv_ant_conf->main_gaintb   = 0;
582                         pdiv_ant_conf->alt_gaintb    = 0;
583                         break;
584                 case (0x03): //A-B A+B
585                         pdiv_ant_conf->fast_div_bias = 0x1;
586                         pdiv_ant_conf->main_gaintb   = 0;
587                         pdiv_ant_conf->alt_gaintb    = 0;
588                         break;
589                 case (0x10): //LNA2 A-B
590                         if ((antcomb->scan == 0)
591                             && (alt_ratio > alt_ant_ratio_th)) {
592                                 pdiv_ant_conf->fast_div_bias = 0x1;
593                         } else {
594                                 pdiv_ant_conf->fast_div_bias = 0x2;
595                         }
596                         pdiv_ant_conf->main_gaintb   = 0;
597                         pdiv_ant_conf->alt_gaintb    = 0;
598                         break;
599                 case (0x12): //LNA2 LNA1
600                         pdiv_ant_conf->fast_div_bias = 0x1;
601                         pdiv_ant_conf->main_gaintb   = 0;
602                         pdiv_ant_conf->alt_gaintb    = 0;
603                         break;
604                 case (0x13): //LNA2 A+B
605                         if ((antcomb->scan == 0)
606                             && (alt_ratio > alt_ant_ratio_th)) {
607                                 pdiv_ant_conf->fast_div_bias = 0x1;
608                         } else {
609                                 pdiv_ant_conf->fast_div_bias = 0x2;
610                         }
611                         pdiv_ant_conf->main_gaintb   = 0;
612                         pdiv_ant_conf->alt_gaintb    = 0;
613                         break;
614                 case (0x20): //LNA1 A-B
615                         if ((antcomb->scan == 0)
616                             && (alt_ratio > alt_ant_ratio_th)) {
617                                 pdiv_ant_conf->fast_div_bias = 0x1;
618                         } else {
619                                 pdiv_ant_conf->fast_div_bias = 0x2;
620                         }
621                         pdiv_ant_conf->main_gaintb   = 0;
622                         pdiv_ant_conf->alt_gaintb    = 0;
623                         break;
624                 case (0x21): //LNA1 LNA2
625                         pdiv_ant_conf->fast_div_bias = 0x1;
626                         pdiv_ant_conf->main_gaintb   = 0;
627                         pdiv_ant_conf->alt_gaintb    = 0;
628                         break;
629                 case (0x23): //LNA1 A+B
630                         if ((antcomb->scan == 0)
631                             && (alt_ratio > alt_ant_ratio_th)) {
632                                 pdiv_ant_conf->fast_div_bias = 0x1;
633                         } else {
634                                 pdiv_ant_conf->fast_div_bias = 0x2;
635                         }
636                         pdiv_ant_conf->main_gaintb   = 0;
637                         pdiv_ant_conf->alt_gaintb    = 0;
638                         break;
639                 case (0x30): //A+B A-B
640                         pdiv_ant_conf->fast_div_bias = 0x1;
641                         pdiv_ant_conf->main_gaintb   = 0;
642                         pdiv_ant_conf->alt_gaintb    = 0;
643                         break;
644                 case (0x31): //A+B LNA2
645                         pdiv_ant_conf->fast_div_bias = 0x1;
646                         pdiv_ant_conf->main_gaintb   = 0;
647                         pdiv_ant_conf->alt_gaintb    = 0;
648                         break;
649                 case (0x32): //A+B LNA1
650                         pdiv_ant_conf->fast_div_bias = 0x1;
651                         pdiv_ant_conf->main_gaintb   = 0;
652                         pdiv_ant_conf->alt_gaintb    = 0;
653                         break;
654                 default:
655                         break;
656                 }
657         } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
658                 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
659                 case (0x01): //A-B LNA2
660                         pdiv_ant_conf->fast_div_bias = 0x3b;
661                         break;
662                 case (0x02): //A-B LNA1
663                         pdiv_ant_conf->fast_div_bias = 0x3d;
664                         break;
665                 case (0x03): //A-B A+B
666                         pdiv_ant_conf->fast_div_bias = 0x1;
667                         break;
668                 case (0x10): //LNA2 A-B
669                         pdiv_ant_conf->fast_div_bias = 0x7;
670                         break;
671                 case (0x12): //LNA2 LNA1
672                         pdiv_ant_conf->fast_div_bias = 0x2;
673                         break;
674                 case (0x13): //LNA2 A+B
675                         pdiv_ant_conf->fast_div_bias = 0x7;
676                         break;
677                 case (0x20): //LNA1 A-B
678                         pdiv_ant_conf->fast_div_bias = 0x6;
679                         break;
680                 case (0x21): //LNA1 LNA2
681                         pdiv_ant_conf->fast_div_bias = 0x0;
682                         break;
683                 case (0x23): //LNA1 A+B
684                         pdiv_ant_conf->fast_div_bias = 0x6;
685                         break;
686                 case (0x30): //A+B A-B
687                         pdiv_ant_conf->fast_div_bias = 0x1;
688                         break;
689                 case (0x31): //A+B LNA2
690                         pdiv_ant_conf->fast_div_bias = 0x3b;
691                         break;
692                 case (0x32): //A+B LNA1
693                         pdiv_ant_conf->fast_div_bias = 0x3d;
694                         break;
695                 default:
696                         break;
697                 }
698         }
699 }
700
701 /*
702  * AR9485/AR933x TODO:
703  * + Select a ratio based on whether RSSI is low or not; but I need
704  *   to figure out what "low_rssi_th" is sourced from.
705  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
706  * + .. and there's likely a bunch of other things to include in this.
707  */
708
709 /* Antenna diversity and combining */
710 void
711 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
712     unsigned long ticks, int hz)
713 {
714         HAL_ANT_COMB_CONFIG div_ant_conf;
715         struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
716         int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
717         int curr_main_set, curr_bias;
718         int main_rssi = rs->rs_rssi_ctl[0];
719         int alt_rssi = rs->rs_rssi_ctl[1];
720         int rx_ant_conf, main_ant_conf, alt_ant_conf;
721         HAL_BOOL short_scan = AH_FALSE;
722
723         rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
724         main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
725         alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
726
727 #if 0
728         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
729             "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
730             "FastDiv: %d\n",
731             __func__,
732             main_rssi,
733             alt_rssi,
734             main_ant_conf,
735             alt_ant_conf,
736             rx_ant_conf,
737             !!(rs->rs_rssi_ctl[2] & 0x80),
738             !!(rs->rs_rssi_ctl[2] & 0x40),
739             !!(rs->rs_rssi_ext[2] & 0x40));
740 #endif
741
742         /*
743          * If LNA diversity combining isn't enabled, don't run this.
744          */
745         if (! sc->sc_dolnadiv)
746                 return;
747
748         /*
749          * XXX this is ugly, but the HAL code attaches the
750          * LNA diversity to the TX antenna settings.
751          * I don't know why.
752          */
753         if (sc->sc_txantenna != HAL_ANT_VARIABLE)
754                 return;
755
756         /* Record packet only when alt_rssi is positive */
757         if (main_rssi > 0 && alt_rssi > 0) {
758                 antcomb->total_pkt_count++;
759                 antcomb->main_total_rssi += main_rssi;
760                 antcomb->alt_total_rssi  += alt_rssi;
761                 if (main_ant_conf == rx_ant_conf)
762                         antcomb->main_recv_cnt++;
763                 else
764                         antcomb->alt_recv_cnt++;
765         }
766
767         /* Short scan check */
768         if (antcomb->scan && antcomb->alt_good) {
769                 if (ieee80211_time_after(ticks, antcomb->scan_start_time +
770                     msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
771                         short_scan = AH_TRUE;
772                 else
773                         if (antcomb->total_pkt_count ==
774                             ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
775                                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
776                                             antcomb->total_pkt_count);
777                                 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
778                                         short_scan = AH_TRUE;
779                         }
780         }
781
782 #if 0
783         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
784             "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
785             __func__,
786             antcomb->total_pkt_count,
787             !! (rs->rs_moreaggr),
788             !! (short_scan));
789 #endif
790
791         if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
792             rs->rs_moreaggr) && !short_scan)
793                 return;
794
795         if (antcomb->total_pkt_count) {
796                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
797                              antcomb->total_pkt_count);
798                 main_rssi_avg = (antcomb->main_total_rssi /
799                                  antcomb->total_pkt_count);
800                 alt_rssi_avg = (antcomb->alt_total_rssi /
801                                  antcomb->total_pkt_count);
802         }
803
804         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
805
806         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
807         curr_alt_set = div_ant_conf.alt_lna_conf;
808         curr_main_set = div_ant_conf.main_lna_conf;
809         curr_bias = div_ant_conf.fast_div_bias;
810
811         antcomb->count++;
812
813         if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
814                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
815                         ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
816                                                   main_rssi_avg);
817                         antcomb->alt_good = AH_TRUE;
818                 } else {
819                         antcomb->alt_good = AH_FALSE;
820                 }
821
822                 antcomb->count = 0;
823                 antcomb->scan = AH_TRUE;
824                 antcomb->scan_not_start = AH_TRUE;
825         }
826
827         if (!antcomb->scan) {
828                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
829                         if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
830                                 /* Switch main and alt LNA */
831                                 div_ant_conf.main_lna_conf =
832                                                 HAL_ANT_DIV_COMB_LNA2;
833                                 div_ant_conf.alt_lna_conf  =
834                                                 HAL_ANT_DIV_COMB_LNA1;
835                         } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
836                                 div_ant_conf.main_lna_conf =
837                                                 HAL_ANT_DIV_COMB_LNA1;
838                                 div_ant_conf.alt_lna_conf  =
839                                                 HAL_ANT_DIV_COMB_LNA2;
840                         }
841
842                         goto div_comb_done;
843                 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
844                            (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
845                         /* Set alt to another LNA */
846                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
847                                 div_ant_conf.alt_lna_conf =
848                                                 HAL_ANT_DIV_COMB_LNA1;
849                         else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
850                                 div_ant_conf.alt_lna_conf =
851                                                 HAL_ANT_DIV_COMB_LNA2;
852
853                         goto div_comb_done;
854                 }
855
856                 if ((alt_rssi_avg < (main_rssi_avg +
857                     antcomb->lna1_lna2_delta)))
858                         goto div_comb_done;
859         }
860
861         if (!antcomb->scan_not_start) {
862                 switch (curr_alt_set) {
863                 case HAL_ANT_DIV_COMB_LNA2:
864                         antcomb->rssi_lna2 = alt_rssi_avg;
865                         antcomb->rssi_lna1 = main_rssi_avg;
866                         antcomb->scan = AH_TRUE;
867                         /* set to A+B */
868                         div_ant_conf.main_lna_conf =
869                                 HAL_ANT_DIV_COMB_LNA1;
870                         div_ant_conf.alt_lna_conf  =
871                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
872                         break;
873                 case HAL_ANT_DIV_COMB_LNA1:
874                         antcomb->rssi_lna1 = alt_rssi_avg;
875                         antcomb->rssi_lna2 = main_rssi_avg;
876                         antcomb->scan = AH_TRUE;
877                         /* set to A+B */
878                         div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
879                         div_ant_conf.alt_lna_conf  =
880                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
881                         break;
882                 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
883                         antcomb->rssi_add = alt_rssi_avg;
884                         antcomb->scan = AH_TRUE;
885                         /* set to A-B */
886                         div_ant_conf.alt_lna_conf =
887                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
888                         break;
889                 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
890                         antcomb->rssi_sub = alt_rssi_avg;
891                         antcomb->scan = AH_FALSE;
892                         if (antcomb->rssi_lna2 >
893                             (antcomb->rssi_lna1 +
894                             ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
895                                 /* use LNA2 as main LNA */
896                                 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
897                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
898                                         /* set to A+B */
899                                         div_ant_conf.main_lna_conf =
900                                                 HAL_ANT_DIV_COMB_LNA2;
901                                         div_ant_conf.alt_lna_conf  =
902                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
903                                 } else if (antcomb->rssi_sub >
904                                            antcomb->rssi_lna1) {
905                                         /* set to A-B */
906                                         div_ant_conf.main_lna_conf =
907                                                 HAL_ANT_DIV_COMB_LNA2;
908                                         div_ant_conf.alt_lna_conf =
909                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
910                                 } else {
911                                         /* set to LNA1 */
912                                         div_ant_conf.main_lna_conf =
913                                                 HAL_ANT_DIV_COMB_LNA2;
914                                         div_ant_conf.alt_lna_conf =
915                                                 HAL_ANT_DIV_COMB_LNA1;
916                                 }
917                         } else {
918                                 /* use LNA1 as main LNA */
919                                 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
920                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
921                                         /* set to A+B */
922                                         div_ant_conf.main_lna_conf =
923                                                 HAL_ANT_DIV_COMB_LNA1;
924                                         div_ant_conf.alt_lna_conf  =
925                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
926                                 } else if (antcomb->rssi_sub >
927                                            antcomb->rssi_lna1) {
928                                         /* set to A-B */
929                                         div_ant_conf.main_lna_conf =
930                                                 HAL_ANT_DIV_COMB_LNA1;
931                                         div_ant_conf.alt_lna_conf =
932                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
933                                 } else {
934                                         /* set to LNA2 */
935                                         div_ant_conf.main_lna_conf =
936                                                 HAL_ANT_DIV_COMB_LNA1;
937                                         div_ant_conf.alt_lna_conf =
938                                                 HAL_ANT_DIV_COMB_LNA2;
939                                 }
940                         }
941                         break;
942                 default:
943                         break;
944                 }
945         } else {
946                 if (!antcomb->alt_good) {
947                         antcomb->scan_not_start = AH_FALSE;
948                         /* Set alt to another LNA */
949                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
950                                 div_ant_conf.main_lna_conf =
951                                                 HAL_ANT_DIV_COMB_LNA2;
952                                 div_ant_conf.alt_lna_conf =
953                                                 HAL_ANT_DIV_COMB_LNA1;
954                         } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
955                                 div_ant_conf.main_lna_conf =
956                                                 HAL_ANT_DIV_COMB_LNA1;
957                                 div_ant_conf.alt_lna_conf =
958                                                 HAL_ANT_DIV_COMB_LNA2;
959                         }
960                         goto div_comb_done;
961                 }
962         }
963
964         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
965                                            main_rssi_avg, alt_rssi_avg,
966                                            alt_ratio);
967
968         antcomb->quick_scan_cnt++;
969
970 div_comb_done:
971 #if 0
972         ath_ant_div_conf_fast_divbias(&div_ant_conf);
973 #endif
974
975         ath_ant_adjust_fast_divbias(antcomb,
976             alt_ratio,
977             ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
978             div_ant_conf.antdiv_configgroup,
979             &div_ant_conf);
980
981         ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
982
983         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
984            __func__, antcomb->total_pkt_count);
985
986         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
987            __func__, antcomb->main_total_rssi);
988         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
989            __func__, antcomb->alt_total_rssi);
990
991         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
992            __func__, main_rssi_avg);
993         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
994            __func__, alt_rssi_avg);
995
996         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
997            __func__, antcomb->main_recv_cnt);
998         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
999            __func__, antcomb->alt_recv_cnt);
1000
1001 //      if (curr_alt_set != div_ant_conf.alt_lna_conf)
1002                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1003                     __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1004 //      if (curr_main_set != div_ant_conf.main_lna_conf)
1005                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1006                     __func__, curr_main_set, div_ant_conf.main_lna_conf);
1007 //      if (curr_bias != div_ant_conf.fast_div_bias)
1008                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1009                     __func__, curr_bias, div_ant_conf.fast_div_bias);
1010
1011         antcomb->scan_start_time = ticks;
1012         antcomb->total_pkt_count = 0;
1013         antcomb->main_total_rssi = 0;
1014         antcomb->alt_total_rssi = 0;
1015         antcomb->main_recv_cnt = 0;
1016         antcomb->alt_recv_cnt = 0;
1017 }
1018