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