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