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